From c49e93d26fd57d68c31a68959c0f85a330ea0fc0 Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 22:57:50 +0700 Subject: [PATCH 1/7] feat(schema): expand blueprint grammar support --- schema/blueprint/blueprint.go | 227 ++++++++++- schema/blueprint/blueprint_test.go | 364 +++++++++++++++++- schema/blueprint/column_definition.go | 8 + schema/blueprint/command.go | 46 ++- .../blueprint/foreign_id_column_definition.go | 108 ++++++ schema/blueprint/grammar.go | 25 ++ schema/grammars/mysql_grammar.go | 111 +++++- schema/grammars/mysql_grammar_test.go | 317 ++++++++++++++- schema/grammars/postgres_grammar.go | 46 ++- schema/grammars/postgres_grammar_test.go | 97 +++++ schema/grammars/sqlite_grammar.go | 14 +- schema/grammars/sqlite_grammar_test.go | 22 ++ 12 files changed, 1324 insertions(+), 61 deletions(-) create mode 100644 schema/blueprint/foreign_id_column_definition.go diff --git a/schema/blueprint/blueprint.go b/schema/blueprint/blueprint.go index 7bafdc5..710a160 100644 --- a/schema/blueprint/blueprint.go +++ b/schema/blueprint/blueprint.go @@ -50,6 +50,7 @@ const ( ColumnTypeInet string = "inet" ColumnTypeMacaddr string = "macaddr" ColumnTypeMacaddr8 string = "macaddr8" + ColumnTypeRaw string = "raw" ) const ( @@ -57,6 +58,9 @@ const ( defaultTimePrecision int = 0 ) +// DefaultMorphKeyType controls the key type used by Morphs/NullableMorphs ("int", "uuid", "ulid"). +var DefaultMorphKeyType = "int" + // Blueprint represents a schema blueprint for creating or altering a database table. type Blueprint struct { Dialect dialect.Dialect @@ -69,7 +73,9 @@ type Blueprint struct { CollationVal string EngineVal string CommentVal string + TemporaryVal bool AutoIncrementStartingValuesVal *int + afterColumn string } // Builder interface. @@ -112,10 +118,12 @@ func (b *Blueprint) Engine(engine string) { func (b *Blueprint) Comment(comment string) { b.CommentVal = comment + b.addCommand(CommandTableComment, &Command{Comment: comment}) } func (b *Blueprint) AutoIncrementStartingValues(value int) { b.AutoIncrementStartingValuesVal = &value + b.addCommand(CommandAutoIncrementStartingValues, &Command{Value: value}) } func (b *Blueprint) Column(name string, columnType string) ColumnDefinition { @@ -280,13 +288,13 @@ func (b *Blueprint) TimestampTz(name string, precision ...int) ColumnDefinition } func (b *Blueprint) Timestamps(precision ...int) { - b.Timestamp("created_at", precision...).UseCurrent() - b.Timestamp("updated_at", precision...).UseCurrent().UseCurrentOnUpdate() + b.Timestamp("created_at", precision...).Nullable() + b.Timestamp("updated_at", precision...).Nullable() } func (b *Blueprint) TimestampsTz(precision ...int) { - b.TimestampTz("created_at", precision...).UseCurrent() - b.TimestampTz("updated_at", precision...).UseCurrent().UseCurrentOnUpdate() + b.TimestampTz("created_at", precision...).Nullable() + b.TimestampTz("updated_at", precision...).Nullable() } func (b *Blueprint) Year(name string) ColumnDefinition { @@ -381,6 +389,177 @@ func (b *Blueprint) MacAddr8(name string) ColumnDefinition { return b.addColumn(ColumnTypeMacaddr8, name) } +func (b *Blueprint) Temporary() { + b.TemporaryVal = true +} + +func (b *Blueprint) InnoDB() { + b.Engine("InnoDB") +} + +func (b *Blueprint) IntegerIncrements(name string) ColumnDefinition { + return b.Increments(name) +} + +func (b *Blueprint) SoftDeletes(name string, precision ...int) ColumnDefinition { + return b.Timestamp(util.Optional("deleted_at", name), precision...).Nullable() +} + +func (b *Blueprint) SoftDeletesTz(name string, precision ...int) ColumnDefinition { + return b.TimestampTz(util.Optional("deleted_at", name), precision...).Nullable() +} + +func (b *Blueprint) SoftDeletesDatetime(name string, precision ...int) ColumnDefinition { + return b.DateTime(util.Optional("deleted_at", name), precision...).Nullable() +} + +func (b *Blueprint) DropSoftDeletes(name ...string) { + b.DropColumn(util.Optional("deleted_at", name...)) +} + +func (b *Blueprint) DropSoftDeletesTz(name ...string) { + b.DropSoftDeletes(name...) +} + +func (b *Blueprint) NullableTimestamps(precision ...int) { + b.Timestamps(precision...) +} + +func (b *Blueprint) NullableTimestampsTz(precision ...int) { + b.TimestampsTz(precision...) +} + +func (b *Blueprint) Datetimes(precision ...int) { + b.DateTime("created_at", precision...).Nullable() + b.DateTime("updated_at", precision...).Nullable() +} + +func (b *Blueprint) RawColumn(name string, definition string) ColumnDefinition { + return b.addColumn(ColumnTypeRaw, name, &Column{RawDefinition: &definition}) +} + +func (b *Blueprint) RawIndex(expression string, name string) IndexDefinition { + return b.indexCommand(CommandIndex, expression).Name(name) +} + +func (b *Blueprint) After(column string, callback func(*Blueprint)) { + b.afterColumn = column + callback(b) + b.afterColumn = "" +} + +func (b *Blueprint) RemoveColumn(name string) { + filtered := b.Columns[:0] + for _, col := range b.Columns { + if col.Name != name { + filtered = append(filtered, col) + } + } + b.Columns = filtered +} + +func (b *Blueprint) SpatialIndex(column string, otherColumns ...string) IndexDefinition { + return b.indexCommand(CommandSpatialIndex, append([]string{column}, otherColumns...)...) +} + +func (b *Blueprint) VectorIndex(column string) IndexDefinition { + return b.indexCommand(CommandVectorIndex, column) +} + +func (b *Blueprint) DropSpatialIndex(index any) { + b.dropIndexCommand(CommandDropSpatialIndex, CommandSpatialIndex, index) +} + +func (b *Blueprint) Morphs(name string, indexName ...string) { + switch DefaultMorphKeyType { + case "uuid": + b.UUIDMorphs(name, indexName...) + case "ulid": + b.ULIDMorphs(name, indexName...) + default: + b.NumericMorphs(name, indexName...) + } +} + +func (b *Blueprint) NullableMorphs(name string, indexName ...string) { + switch DefaultMorphKeyType { + case "uuid": + b.NullableUUIDMorphs(name, indexName...) + case "ulid": + b.NullableULIDMorphs(name, indexName...) + default: + b.NullableNumericMorphs(name, indexName...) + } +} + +func (b *Blueprint) NumericMorphs(name string, indexName ...string) { + b.String(name + "_type") + b.UnsignedBigInteger(name + "_id") + b.Index(name+"_type", name+"_id").Name(util.Optional("", indexName...)) +} + +func (b *Blueprint) NullableNumericMorphs(name string, indexName ...string) { + b.String(name + "_type").Nullable() + b.UnsignedBigInteger(name + "_id").Nullable() + b.Index(name+"_type", name+"_id").Name(util.Optional("", indexName...)) +} + +func (b *Blueprint) UUIDMorphs(name string, indexName ...string) { + b.String(name + "_type") + b.UUID(name + "_id") + b.Index(name+"_type", name+"_id").Name(util.Optional("", indexName...)) +} + +func (b *Blueprint) NullableUUIDMorphs(name string, indexName ...string) { + b.String(name + "_type").Nullable() + b.UUID(name + "_id").Nullable() + b.Index(name+"_type", name+"_id").Name(util.Optional("", indexName...)) +} + +func (b *Blueprint) ULIDMorphs(name string, indexName ...string) { + b.String(name + "_type") + b.ULID(name + "_id") + b.Index(name+"_type", name+"_id").Name(util.Optional("", indexName...)) +} + +func (b *Blueprint) NullableULIDMorphs(name string, indexName ...string) { + b.String(name + "_type").Nullable() + b.ULID(name + "_id").Nullable() + b.Index(name+"_type", name+"_id").Name(util.Optional("", indexName...)) +} + +func (b *Blueprint) DropMorphs(name string, indexName ...string) { + idx := util.Optional("", indexName...) + if idx == "" { + idx = b.Grammar.CreateIndexName(b, "index", name+"_type", name+"_id") + } + b.DropIndex(idx) + b.DropColumn(name+"_type", name+"_id") +} + +func (b *Blueprint) ForeignID(name string) ForeignIDColumnDefinition { + col := b.addColumn(ColumnTypeBigInteger, name) + col.UnsignedVal = util.PtrOf(true) + return &foreignIDColumnDefinition{column: col, blueprint: b} +} + +func (b *Blueprint) ForeignUUID(name string) ForeignIDColumnDefinition { + col := b.addColumn(ColumnTypeUUID, name) + return &foreignIDColumnDefinition{column: col, blueprint: b} +} + +func (b *Blueprint) ForeignULID(name string, length ...int) ForeignIDColumnDefinition { + col := b.addColumn(ColumnTypeChar, name, &Column{ + Length: util.OptionalPtr(26, length...), + }) + return &foreignIDColumnDefinition{column: col, blueprint: b} +} + +func (b *Blueprint) DropConstrainedForeignID(name string) { + b.DropForeign([]string{name}) + b.DropColumn(name) +} + func (b *Blueprint) DropTimestamps() { b.DropColumn("created_at", "updated_at") } @@ -660,21 +839,26 @@ func (b *Blueprint) ToSQL() ([]string, error) { CommandDropIfExists: b.Grammar.CompileDropIfExists, } secondaryCommandMap := map[string]func(blueprint *Blueprint, command *Command) (string, error){ - CommandChange: b.Grammar.CompileChange, - CommandDropColumn: b.Grammar.CompileDropColumn, - CommandDropIndex: b.Grammar.CompileDropIndex, - CommandDropForeign: b.Grammar.CompileDropForeign, - CommandDropFullText: b.Grammar.CompileDropFulltext, - CommandDropPrimary: b.Grammar.CompileDropPrimary, - CommandDropUnique: b.Grammar.CompileDropUnique, - CommandForeign: b.Grammar.CompileForeign, - CommandFullText: b.Grammar.CompileFullText, - CommandIndex: b.Grammar.CompileIndex, - CommandPrimary: b.Grammar.CompilePrimary, - CommandRename: b.Grammar.CompileRename, - CommandRenameColumn: b.Grammar.CompileRenameColumn, - CommandRenameIndex: b.Grammar.CompileRenameIndex, - CommandUnique: b.Grammar.CompileUnique, + CommandChange: b.Grammar.CompileChange, + CommandDropColumn: b.Grammar.CompileDropColumn, + CommandDropIndex: b.Grammar.CompileDropIndex, + CommandDropForeign: b.Grammar.CompileDropForeign, + CommandDropFullText: b.Grammar.CompileDropFulltext, + CommandDropPrimary: b.Grammar.CompileDropPrimary, + CommandDropUnique: b.Grammar.CompileDropUnique, + CommandDropSpatialIndex: b.Grammar.CompileDropSpatialIndex, + CommandForeign: b.Grammar.CompileForeign, + CommandFullText: b.Grammar.CompileFullText, + CommandIndex: b.Grammar.CompileIndex, + CommandPrimary: b.Grammar.CompilePrimary, + CommandRename: b.Grammar.CompileRename, + CommandRenameColumn: b.Grammar.CompileRenameColumn, + CommandRenameIndex: b.Grammar.CompileRenameIndex, + CommandUnique: b.Grammar.CompileUnique, + CommandSpatialIndex: b.Grammar.CompileSpatialIndex, + CommandVectorIndex: b.Grammar.CompileVectorIndex, + CommandTableComment: b.Grammar.CompileTableComment, + CommandAutoIncrementStartingValues: b.Grammar.CompileAutoIncrementStartingValues, } for _, cmd := range b.Commands { if compileFunc, exists := mainCommandMap[cmd.Name]; exists { @@ -720,6 +904,11 @@ func (b *Blueprint) addColumn(colType string, name string, columnDefs ...*Column func (b *Blueprint) addColumnDefinition(col *Column) *Column { b.Columns = append(b.Columns, col) + if b.afterColumn != "" { + after := b.afterColumn + col.AfterVal = &after + b.afterColumn = col.Name + } return col } diff --git a/schema/blueprint/blueprint_test.go b/schema/blueprint/blueprint_test.go index 8b98ea2..270e358 100644 --- a/schema/blueprint/blueprint_test.go +++ b/schema/blueprint/blueprint_test.go @@ -85,12 +85,11 @@ func TestBlueprint_ColumnAddition(t *testing.T) { assert.Equal(t, "created_at", columns[4].Name) assert.Equal(t, blueprint.ColumnTypeTimestamp, columns[4].ColumnType) - assert.True(t, columns[4].UseCurrentVal) + assert.True(t, *columns[4].NullableVal) assert.Equal(t, "updated_at", columns[5].Name) assert.Equal(t, blueprint.ColumnTypeTimestamp, columns[5].ColumnType) - assert.True(t, columns[5].UseCurrentVal) - assert.True(t, columns[5].UseCurrentOnUpdateVal) + assert.True(t, *columns[5].NullableVal) } func TestBlueprint_Indexes(t *testing.T) { @@ -355,3 +354,362 @@ func TestBlueprint_ToSQL_Errors(t *testing.T) { _, err := bp.ToSQL() assert.Error(t, err) } + +func TestBlueprint_Temporary(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("logs", &mockGrammar{}) + assert.False(t, bp.TemporaryVal) + bp.Temporary() + assert.True(t, bp.TemporaryVal) +} + +func TestBlueprint_InnoDB(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.InnoDB() + assert.Equal(t, "InnoDB", bp.EngineVal) +} + +func TestBlueprint_SoftDeletes(t *testing.T) { + t.Run("SoftDeletes adds nullable deleted_at timestamp", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.SoftDeletes("deleted_at") + require.Len(t, bp.Columns, 1) + assert.Equal(t, "deleted_at", bp.Columns[0].Name) + assert.Equal(t, blueprint.ColumnTypeTimestamp, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].NullableVal) + }) + + t.Run("SoftDeletes custom name", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.SoftDeletes("removed_at") + assert.Equal(t, "removed_at", bp.Columns[0].Name) + }) + + t.Run("SoftDeletesTz adds nullable timestamptz", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.SoftDeletesTz("deleted_at") + assert.Equal(t, blueprint.ColumnTypeTimestampTz, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].NullableVal) + }) + + t.Run("SoftDeletesDatetime adds nullable datetime", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.SoftDeletesDatetime("deleted_at") + assert.Equal(t, blueprint.ColumnTypeDateTime, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].NullableVal) + }) + + t.Run("DropSoftDeletes adds dropColumn command", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.DropSoftDeletes() + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandDropColumn, bp.Commands[0].Name) + assert.Equal(t, []string{"deleted_at"}, bp.Commands[0].Columns) + }) + + t.Run("DropSoftDeletesTz delegates to DropSoftDeletes", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.DropSoftDeletesTz() + require.Len(t, bp.Commands, 1) + assert.Equal(t, []string{"deleted_at"}, bp.Commands[0].Columns) + }) +} + +func TestBlueprint_NullableTimestampHelpers(t *testing.T) { + t.Run("NullableTimestamps produces nullable created_at and updated_at", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.NullableTimestamps() + require.Len(t, bp.Columns, 2) + assert.Equal(t, "created_at", bp.Columns[0].Name) + assert.True(t, *bp.Columns[0].NullableVal) + assert.Equal(t, "updated_at", bp.Columns[1].Name) + assert.True(t, *bp.Columns[1].NullableVal) + }) + + t.Run("NullableTimestampsTz produces nullable timestamptz columns", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.NullableTimestampsTz() + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeTimestampTz, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].NullableVal) + }) + + t.Run("Datetimes produces nullable datetime columns", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.Datetimes() + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeDateTime, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].NullableVal) + assert.Equal(t, blueprint.ColumnTypeDateTime, bp.Columns[1].ColumnType) + assert.True(t, *bp.Columns[1].NullableVal) + }) +} + +func TestBlueprint_RawColumn(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("logs", &mockGrammar{}) + bp.RawColumn("payload", "MEDIUMBLOB NOT NULL") + require.Len(t, bp.Columns, 1) + assert.Equal(t, "payload", bp.Columns[0].Name) + assert.Equal(t, blueprint.ColumnTypeRaw, bp.Columns[0].ColumnType) + assert.Equal(t, "MEDIUMBLOB NOT NULL", *bp.Columns[0].RawDefinition) +} + +func TestBlueprint_RemoveColumn(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.String("email") + bp.String("name") + bp.String("phone") + require.Len(t, bp.Columns, 3) + + bp.RemoveColumn("name") + require.Len(t, bp.Columns, 2) + assert.Equal(t, "email", bp.Columns[0].Name) + assert.Equal(t, "phone", bp.Columns[1].Name) +} + +func TestBlueprint_SpatialVectorIndex(t *testing.T) { + t.Run("SpatialIndex adds spatialIndex command", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("locations", &mockGrammar{}) + bp.SpatialIndex("geom") + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandSpatialIndex, bp.Commands[0].Name) + assert.Equal(t, []string{"geom"}, bp.Commands[0].Columns) + }) + + t.Run("SpatialIndex with name", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("locations", &mockGrammar{}) + bp.SpatialIndex("geom").Name("idx_geom") + assert.Equal(t, "idx_geom", bp.Commands[0].Index) + }) + + t.Run("VectorIndex adds vectorIndex command", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("items", &mockGrammar{}) + bp.VectorIndex("embedding") + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandVectorIndex, bp.Commands[0].Name) + assert.Equal(t, []string{"embedding"}, bp.Commands[0].Columns) + }) + + t.Run("DropSpatialIndex adds dropSpatialIndex command", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("locations", &mockGrammar{}) + bp.DropSpatialIndex("idx_geom") + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandDropSpatialIndex, bp.Commands[0].Name) + assert.Equal(t, "idx_geom", bp.Commands[0].Index) + }) +} + +func TestBlueprint_Morphs(t *testing.T) { + t.Run("NumericMorphs adds type+id columns and index", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NumericMorphs("commentable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, "commentable_type", bp.Columns[0].Name) + assert.Equal(t, "commentable_id", bp.Columns[1].Name) + assert.Equal(t, blueprint.ColumnTypeBigInteger, bp.Columns[1].ColumnType) + assert.True(t, *bp.Columns[1].UnsignedVal) + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandIndex, bp.Commands[0].Name) + }) + + t.Run("NullableNumericMorphs adds nullable columns", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NullableNumericMorphs("commentable") + assert.True(t, *bp.Columns[0].NullableVal) + assert.True(t, *bp.Columns[1].NullableVal) + }) + + t.Run("UUIDMorphs adds uuid id column", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.UUIDMorphs("commentable") + assert.Equal(t, blueprint.ColumnTypeUUID, bp.Columns[1].ColumnType) + }) + + t.Run("ULIDMorphs adds ulid id column", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.ULIDMorphs("commentable") + assert.Equal(t, blueprint.ColumnTypeULID, bp.Columns[1].ColumnType) + }) + + t.Run("Morphs with default key type delegates to NumericMorphs", func(t *testing.T) { + // DefaultMorphKeyType defaults to "int" → NumericMorphs + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NumericMorphs("commentable") + assert.Equal(t, blueprint.ColumnTypeBigInteger, bp.Columns[1].ColumnType) + }) + + t.Run("UUIDMorphs produces uuid id", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.UUIDMorphs("commentable") + assert.Equal(t, blueprint.ColumnTypeUUID, bp.Columns[1].ColumnType) + }) + + t.Run("DropMorphs drops columns and index", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.DropMorphs("commentable") + require.Len(t, bp.Commands, 2) + assert.Equal(t, blueprint.CommandDropIndex, bp.Commands[0].Name) + assert.Equal(t, blueprint.CommandDropColumn, bp.Commands[1].Name) + assert.Equal(t, []string{"commentable_type", "commentable_id"}, bp.Commands[1].Columns) + }) +} + +func TestBlueprint_ForeignID(t *testing.T) { + t.Run("ForeignID adds bigint unsigned column", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("posts", &mockGrammar{}) + bp.ForeignID("user_id") + require.Len(t, bp.Columns, 1) + assert.Equal(t, "user_id", bp.Columns[0].Name) + assert.Equal(t, blueprint.ColumnTypeBigInteger, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].UnsignedVal) + }) + + t.Run("ForeignID Constrained infers table name", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("posts", &mockGrammar{}) + bp.ForeignID("user_id").Constrained() + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandForeign, bp.Commands[0].Name) + assert.Equal(t, "users", bp.Commands[0].On) + assert.Equal(t, []string{"id"}, bp.Commands[0].References) + }) + + t.Run("ForeignID Constrained with explicit table", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("posts", &mockGrammar{}) + bp.ForeignID("author_id").Constrained("accounts") + assert.Equal(t, "accounts", bp.Commands[0].On) + }) + + t.Run("ForeignUUID adds uuid column", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("posts", &mockGrammar{}) + bp.ForeignUUID("user_id") + assert.Equal(t, blueprint.ColumnTypeUUID, bp.Columns[0].ColumnType) + }) + + t.Run("ForeignULID adds char(26) column", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("posts", &mockGrammar{}) + bp.ForeignULID("user_id") + assert.Equal(t, blueprint.ColumnTypeChar, bp.Columns[0].ColumnType) + assert.Equal(t, 26, *bp.Columns[0].Length) + }) + + t.Run("DropConstrainedForeignID drops foreign and column", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("posts", &mockGrammar{}) + bp.DropConstrainedForeignID("user_id") + require.Len(t, bp.Commands, 2) + assert.Equal(t, blueprint.CommandDropForeign, bp.Commands[0].Name) + assert.Equal(t, blueprint.CommandDropColumn, bp.Commands[1].Name) + }) +} + +func TestBlueprint_After(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.String("email") + bp.After("email", func(b *blueprint.Blueprint) { + b.String("phone") + b.String("bio") + }) + require.Len(t, bp.Columns, 3) + assert.Equal(t, "email", bp.Columns[0].Name) + // phone positioned after email, bio positioned after phone (chained) + assert.Equal(t, "phone", bp.Columns[1].Name) + assert.Equal(t, "email", *bp.Columns[1].AfterVal) + assert.Equal(t, "bio", bp.Columns[2].Name) + assert.Equal(t, "phone", *bp.Columns[2].AfterVal) +} + +func TestBlueprint_RawIndex(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.RawIndex("(lower(email))", "idx_email_lower") + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandIndex, bp.Commands[0].Name) + assert.Equal(t, []string{"(lower(email))"}, bp.Commands[0].Columns) + assert.Equal(t, "idx_email_lower", bp.Commands[0].Index) +} + +func TestBlueprint_IntegerIncrements(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("users", &mockGrammar{}) + bp.IntegerIncrements("id") + require.Len(t, bp.Columns, 1) + assert.Equal(t, "id", bp.Columns[0].Name) + assert.Equal(t, blueprint.ColumnTypeInteger, bp.Columns[0].ColumnType) + assert.True(t, *bp.Columns[0].UnsignedVal) + assert.True(t, *bp.Columns[0].AutoIncrementVal) +} + +func TestBlueprint_Morphs_KeyTypeDispatch(t *testing.T) { + original := blueprint.DefaultMorphKeyType + t.Cleanup(func() { + blueprint.DefaultMorphKeyType = original //nolint:reassign // Tests verify global morph-key dispatch. + }) + + t.Run("int dispatches to NumericMorphs", func(t *testing.T) { + blueprint.DefaultMorphKeyType = "int" //nolint:reassign // Tests verify global morph-key dispatch. + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.Morphs("commentable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeBigInteger, bp.Columns[1].ColumnType) + assert.True(t, *bp.Columns[1].UnsignedVal) + }) + + t.Run("uuid dispatches to UUIDMorphs", func(t *testing.T) { + blueprint.DefaultMorphKeyType = "uuid" //nolint:reassign // Tests verify global morph-key dispatch. + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.Morphs("commentable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeUUID, bp.Columns[1].ColumnType) + }) + + t.Run("ulid dispatches to ULIDMorphs", func(t *testing.T) { + blueprint.DefaultMorphKeyType = "ulid" //nolint:reassign // Tests verify global morph-key dispatch. + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.Morphs("commentable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeULID, bp.Columns[1].ColumnType) + }) +} + +func TestBlueprint_NullableMorphs(t *testing.T) { + original := blueprint.DefaultMorphKeyType + t.Cleanup(func() { + blueprint.DefaultMorphKeyType = original //nolint:reassign // Tests verify global morph-key dispatch. + }) + + t.Run("NullableMorphs delegates to NullableNumericMorphs by default", func(t *testing.T) { + blueprint.DefaultMorphKeyType = "int" //nolint:reassign // Tests verify global morph-key dispatch. + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NullableMorphs("taggable") + require.Len(t, bp.Columns, 2) + assert.True(t, *bp.Columns[0].NullableVal) + assert.True(t, *bp.Columns[1].NullableVal) + assert.Equal(t, blueprint.ColumnTypeBigInteger, bp.Columns[1].ColumnType) + }) + + t.Run("NullableUUIDMorphs adds nullable uuid columns and index", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NullableUUIDMorphs("taggable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, "taggable_type", bp.Columns[0].Name) + assert.True(t, *bp.Columns[0].NullableVal) + assert.Equal(t, "taggable_id", bp.Columns[1].Name) + assert.Equal(t, blueprint.ColumnTypeUUID, bp.Columns[1].ColumnType) + assert.True(t, *bp.Columns[1].NullableVal) + require.Len(t, bp.Commands, 1) + assert.Equal(t, blueprint.CommandIndex, bp.Commands[0].Name) + }) + + t.Run("NullableULIDMorphs adds nullable ulid columns and index", func(t *testing.T) { + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NullableULIDMorphs("taggable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeULID, bp.Columns[1].ColumnType) + assert.True(t, *bp.Columns[1].NullableVal) + }) + + t.Run("NullableMorphs with uuid key type", func(t *testing.T) { + blueprint.DefaultMorphKeyType = "uuid" //nolint:reassign // Tests verify global morph-key dispatch. + bp := blueprint.NewBlueprintForTesting("comments", &mockGrammar{}) + bp.NullableMorphs("taggable") + require.Len(t, bp.Columns, 2) + assert.Equal(t, blueprint.ColumnTypeUUID, bp.Columns[1].ColumnType) + assert.True(t, *bp.Columns[1].NullableVal) + }) +} diff --git a/schema/blueprint/column_definition.go b/schema/blueprint/column_definition.go index 6863cd1..df6b34f 100644 --- a/schema/blueprint/column_definition.go +++ b/schema/blueprint/column_definition.go @@ -14,6 +14,7 @@ type ColumnDefinition interface { Collation(collation string) ColumnDefinition Comment(comment string) ColumnDefinition Default(value any) ColumnDefinition + Fixed() ColumnDefinition Index(params ...any) ColumnDefinition Nullable(value ...bool) ColumnDefinition OnUpdate(value any) ColumnDefinition @@ -63,6 +64,8 @@ type Column struct { Srid *int // for geography and geometry types AfterVal *string FirstVal bool + FixedVal *bool + RawDefinition *string VirtualAsVal *string StoredAsVal *string InvisibleVal *bool @@ -223,3 +226,8 @@ func (c *Column) RenameTo(name string) ColumnDefinition { c.RenameToVal = name return c } + +func (c *Column) Fixed() ColumnDefinition { + c.FixedVal = util.PtrOf(true) + return c +} diff --git a/schema/blueprint/command.go b/schema/blueprint/command.go index 0774402..db298af 100644 --- a/schema/blueprint/command.go +++ b/schema/blueprint/command.go @@ -1,25 +1,30 @@ package blueprint const ( - CommandAdd string = "add" - CommandCreate string = "create" - CommandChange string = "change" - CommandDrop string = "drop" - CommandDropIfExists string = "dropIfExists" - CommandRename string = "rename" - CommandDropColumn string = "dropColumn" - CommandRenameColumn string = "renameColumn" - CommandIndex string = "index" - CommandUnique string = "unique" - CommandPrimary string = "primary" - CommandFullText string = "fulltext" - CommandDropIndex string = "dropIndex" - CommandDropUnique string = "dropUnique" - CommandDropPrimary string = "dropPrimary" - CommandDropFullText string = "dropFulltext" - CommandRenameIndex string = "renameIndex" - CommandForeign string = "foreign" - CommandDropForeign string = "dropForeign" + CommandAdd string = "add" + CommandCreate string = "create" + CommandChange string = "change" + CommandDrop string = "drop" + CommandDropIfExists string = "dropIfExists" + CommandRename string = "rename" + CommandDropColumn string = "dropColumn" + CommandRenameColumn string = "renameColumn" + CommandIndex string = "index" + CommandUnique string = "unique" + CommandPrimary string = "primary" + CommandFullText string = "fulltext" + CommandSpatialIndex string = "spatialIndex" + CommandVectorIndex string = "vectorIndex" + CommandDropIndex string = "dropIndex" + CommandDropUnique string = "dropUnique" + CommandDropPrimary string = "dropPrimary" + CommandDropFullText string = "dropFulltext" + CommandDropSpatialIndex string = "dropSpatialIndex" + CommandRenameIndex string = "renameIndex" + CommandForeign string = "foreign" + CommandDropForeign string = "dropForeign" + CommandTableComment string = "tableComment" + CommandAutoIncrementStartingValues string = "autoIncrementStartingValues" ) // Command represents a database command to be executed in a blueprint. @@ -30,11 +35,14 @@ type Command struct { Column *Column Columns []string Algorithm string + OperatorClass string On string OnDelete string OnUpdate string References []string Index string + Comment string + Value int Deferrable *bool InitiallyImmediate *bool Language string diff --git a/schema/blueprint/foreign_id_column_definition.go b/schema/blueprint/foreign_id_column_definition.go new file mode 100644 index 0000000..1377ad7 --- /dev/null +++ b/schema/blueprint/foreign_id_column_definition.go @@ -0,0 +1,108 @@ +package blueprint + +import "strings" + +// ForeignIDColumnDefinition extends ColumnDefinition with FK constraint helpers. +type ForeignIDColumnDefinition interface { + ColumnDefinition + Constrained(table ...string) ForeignKeyDefinition +} + +type foreignIDColumnDefinition struct { + column *Column + blueprint *Blueprint +} + +var _ ForeignIDColumnDefinition = &foreignIDColumnDefinition{} + +func (f *foreignIDColumnDefinition) Constrained(table ...string) ForeignKeyDefinition { + var tableName string + if len(table) > 0 && table[0] != "" { + tableName = table[0] + } else { + tableName = strings.TrimSuffix(f.column.Name, "_id") + "s" + } + return f.blueprint.Foreign(f.column.Name).References("id").On(tableName) +} + +func (f *foreignIDColumnDefinition) GetColumn() *Column { return f.column } + +func (f *foreignIDColumnDefinition) AutoIncrement() ColumnDefinition { + return f.column.AutoIncrement() +} + +func (f *foreignIDColumnDefinition) Change() ColumnDefinition { return f.column.Change() } + +func (f *foreignIDColumnDefinition) Charset(charset string) ColumnDefinition { + return f.column.Charset(charset) +} + +func (f *foreignIDColumnDefinition) Collation(collation string) ColumnDefinition { + return f.column.Collation(collation) +} + +func (f *foreignIDColumnDefinition) Comment(comment string) ColumnDefinition { + return f.column.Comment(comment) +} + +func (f *foreignIDColumnDefinition) Default(value any) ColumnDefinition { + return f.column.Default(value) +} + +func (f *foreignIDColumnDefinition) Fixed() ColumnDefinition { return f.column.Fixed() } + +func (f *foreignIDColumnDefinition) Index(params ...any) ColumnDefinition { + return f.column.Index(params...) +} + +func (f *foreignIDColumnDefinition) Nullable(value ...bool) ColumnDefinition { + return f.column.Nullable(value...) +} + +func (f *foreignIDColumnDefinition) OnUpdate(value any) ColumnDefinition { + return f.column.OnUpdate(value) +} + +func (f *foreignIDColumnDefinition) Primary(value ...bool) ColumnDefinition { + return f.column.Primary(value...) +} + +func (f *foreignIDColumnDefinition) Unique(params ...any) ColumnDefinition { + return f.column.Unique(params...) +} + +func (f *foreignIDColumnDefinition) Unsigned() ColumnDefinition { return f.column.Unsigned() } + +func (f *foreignIDColumnDefinition) UseCurrent() ColumnDefinition { return f.column.UseCurrent() } + +func (f *foreignIDColumnDefinition) UseCurrentOnUpdate() ColumnDefinition { + return f.column.UseCurrentOnUpdate() +} + +func (f *foreignIDColumnDefinition) After(column string) ColumnDefinition { + return f.column.After(column) +} + +func (f *foreignIDColumnDefinition) First() ColumnDefinition { return f.column.First() } + +func (f *foreignIDColumnDefinition) VirtualAs(expression string) ColumnDefinition { + return f.column.VirtualAs(expression) +} + +func (f *foreignIDColumnDefinition) StoredAs(expression string) ColumnDefinition { + return f.column.StoredAs(expression) +} + +func (f *foreignIDColumnDefinition) Invisible() ColumnDefinition { return f.column.Invisible() } + +func (f *foreignIDColumnDefinition) GeneratedAs(expression ...string) ColumnDefinition { + return f.column.GeneratedAs(expression...) +} + +func (f *foreignIDColumnDefinition) Always(value ...bool) ColumnDefinition { + return f.column.Always(value...) +} + +func (f *foreignIDColumnDefinition) RenameTo(name string) ColumnDefinition { + return f.column.RenameTo(name) +} diff --git a/schema/blueprint/grammar.go b/schema/blueprint/grammar.go index 9cbdc14..556e409 100644 --- a/schema/blueprint/grammar.go +++ b/schema/blueprint/grammar.go @@ -27,13 +27,18 @@ type Grammar interface { CompileUnique(blueprint *Blueprint, command *Command) (string, error) CompilePrimary(blueprint *Blueprint, command *Command) (string, error) CompileFullText(blueprint *Blueprint, command *Command) (string, error) + CompileSpatialIndex(blueprint *Blueprint, command *Command) (string, error) + CompileVectorIndex(blueprint *Blueprint, command *Command) (string, error) CompileDropIndex(blueprint *Blueprint, command *Command) (string, error) CompileDropUnique(blueprint *Blueprint, command *Command) (string, error) CompileDropFulltext(blueprint *Blueprint, command *Command) (string, error) CompileDropPrimary(blueprint *Blueprint, command *Command) (string, error) + CompileDropSpatialIndex(blueprint *Blueprint, command *Command) (string, error) CompileRenameIndex(blueprint *Blueprint, command *Command) (string, error) CompileForeign(blueprint *Blueprint, command *Command) (string, error) CompileDropForeign(blueprint *Blueprint, command *Command) (string, error) + CompileTableComment(blueprint *Blueprint, command *Command) (string, error) + CompileAutoIncrementStartingValues(blueprint *Blueprint, command *Command) (string, error) GetFluentCommands() []func(blueprint *Blueprint, command *Command) string GetTableFluentCommands() []func(blueprint *Blueprint) string CreateIndexName(blueprint *Blueprint, idxType string, columns ...string) string @@ -94,6 +99,26 @@ func (g *BaseGrammar) CompileDropForeign(_ *Blueprint, _ *Command) (string, erro return "", errors.New("drop foreign operation not supported by this grammar") } +func (g *BaseGrammar) CompileSpatialIndex(_ *Blueprint, _ *Command) (string, error) { + return "", errors.New("spatial index operation not supported by this grammar") +} + +func (g *BaseGrammar) CompileVectorIndex(_ *Blueprint, _ *Command) (string, error) { + return "", errors.New("vector index operation not supported by this grammar") +} + +func (g *BaseGrammar) CompileDropSpatialIndex(_ *Blueprint, _ *Command) (string, error) { + return "", errors.New("drop spatial index operation not supported by this grammar") +} + +func (g *BaseGrammar) CompileTableComment(_ *Blueprint, _ *Command) (string, error) { + return "", errors.New("table comment operation not supported by this grammar") +} + +func (g *BaseGrammar) CompileAutoIncrementStartingValues(_ *Blueprint, _ *Command) (string, error) { + return "", nil +} + func (g *BaseGrammar) CompileForeign(blueprint *Blueprint, command *Command) (string, error) { if len(command.Columns) == 0 || slices.Contains(command.Columns, "") || command.On == "" || len(command.References) == 0 || slices.Contains(command.References, "") { diff --git a/schema/grammars/mysql_grammar.go b/schema/grammars/mysql_grammar.go index 0399811..63497e1 100644 --- a/schema/grammars/mysql_grammar.go +++ b/schema/grammars/mysql_grammar.go @@ -71,7 +71,11 @@ func (g *mysqlGrammar) CompileCreate(bp *blueprint.Blueprint) (string, error) { } columns = append(columns, g.getConstraints(bp)...) - sql := fmt.Sprintf("CREATE TABLE %s (%s)", g.WrapTable(bp.Name, "`"), strings.Join(columns, ", ")) + create := "CREATE TABLE" + if bp.TemporaryVal { + create = "CREATE TEMPORARY TABLE" + } + sql := fmt.Sprintf("%s %s (%s)", create, g.WrapTable(bp.Name, "`"), strings.Join(columns, ", ")) sql += g.compileTableModifiers(bp) return sql, nil @@ -120,14 +124,6 @@ func (g *mysqlGrammar) CompileChange(bp *blueprint.Blueprint, command *blueprint return sql, nil } -func (g *mysqlGrammar) CompileRename(blueprint *blueprint.Blueprint, command *blueprint.Command) (string, error) { - return fmt.Sprintf( - "ALTER TABLE %s RENAME TO %s", - g.WrapTable(blueprint.Name, "`"), - g.WrapTable(command.To, "`"), - ), nil -} - func (g *mysqlGrammar) CompileDrop(blueprint *blueprint.Blueprint) (string, error) { if blueprint.Name == "" { return "", errors.New("table name cannot be empty") @@ -406,6 +402,7 @@ func (g *mysqlGrammar) getTypeFuncMap() map[string]func(*blueprint.Column) strin blueprint.ColumnTypeGeography: g.typeGeography, blueprint.ColumnTypeGeometry: g.typeGeometry, blueprint.ColumnTypePoint: g.typePoint, + blueprint.ColumnTypeRaw: g.typeRaw, } } @@ -454,7 +451,10 @@ func (g *mysqlGrammar) typeTinyInteger(_ *blueprint.Column) string { } func (g *mysqlGrammar) typeFloat(col *blueprint.Column) string { - return fmt.Sprintf("FLOAT(%d)", *col.Precision) + if col.Precision != nil && *col.Precision > 0 { + return fmt.Sprintf("FLOAT(%d)", *col.Precision) + } + return "FLOAT" } func (g *mysqlGrammar) typeDouble(_ *blueprint.Column) string { @@ -526,7 +526,13 @@ func (g *mysqlGrammar) typeYear(_ *blueprint.Column) string { return "YEAR" } -func (g *mysqlGrammar) typeBinary(_ *blueprint.Column) string { +func (g *mysqlGrammar) typeBinary(col *blueprint.Column) string { + if col.Length != nil && *col.Length > 0 { + if col.FixedVal != nil && *col.FixedVal { + return fmt.Sprintf("BINARY(%d)", *col.Length) + } + return fmt.Sprintf("VARBINARY(%d)", *col.Length) + } return "BLOB" } @@ -546,7 +552,10 @@ func (g *mysqlGrammar) typeMacAddress(_ *blueprint.Column) string { return "VARCHAR(17)" } -func (g *mysqlGrammar) typeVector(_ *blueprint.Column) string { +func (g *mysqlGrammar) typeVector(col *blueprint.Column) string { + if col.Places != nil { + return fmt.Sprintf("VECTOR(%d)", *col.Places) + } return "VECTOR" } @@ -591,6 +600,13 @@ func (g *mysqlGrammar) typePoint(col *blueprint.Column) string { return "POINT" } +func (g *mysqlGrammar) typeRaw(col *blueprint.Column) string { + if col.RawDefinition != nil { + return *col.RawDefinition + } + return "" +} + func (g *mysqlGrammar) modifiers() []func(*blueprint.Column) string { return []func(*blueprint.Column) string{ g.modifyUnsigned, @@ -602,6 +618,7 @@ func (g *mysqlGrammar) modifiers() []func(*blueprint.Column) string { g.modifyDefault, g.modifyIncrement, g.modifyOnUpdate, + g.modifyInvisible, g.modifyComment, g.modifyAfter, g.modifyFirst, @@ -626,6 +643,12 @@ func (g *mysqlGrammar) compileTableModifiers(bp *blueprint.Blueprint) string { } func (g *mysqlGrammar) modifyNullable(col *blueprint.Column) string { + if col.VirtualAsVal != nil || col.StoredAsVal != nil { + if col.NullableVal != nil && !*col.NullableVal { + return " NOT NULL" + } + return "" + } if col.NullableVal != nil && *col.NullableVal { return " NULL" } @@ -647,7 +670,10 @@ func (g *mysqlGrammar) modifyDefault(col *blueprint.Column) string { func (g *mysqlGrammar) modifyIncrement(col *blueprint.Column) string { if slices.Contains(g.serials, col.ColumnType) && col.AutoIncrementVal != nil && *col.AutoIncrementVal { - return " AUTO_INCREMENT" + if col.PrimaryVal != nil || col.ChangeVal { + return " AUTO_INCREMENT" + } + return " AUTO_INCREMENT PRIMARY KEY" } return "" } @@ -718,6 +744,65 @@ func (g *mysqlGrammar) modifyStoredAs(col *blueprint.Column) string { return "" } +func (g *mysqlGrammar) modifyInvisible(col *blueprint.Column) string { + if col.InvisibleVal != nil && *col.InvisibleVal { + return " INVISIBLE" + } + return "" +} + +func (g *mysqlGrammar) CompileSpatialIndex(bp *blueprint.Blueprint, command *blueprint.Command) (string, error) { + if len(command.Columns) == 0 || slices.Contains(command.Columns, "") { + return "", errors.New("spatial index columns cannot be empty") + } + index := g.WrapIndexName(command.Index, "`") + if index == "" { + index = g.WrapIndexName(g.CreateIndexName(bp, "spatialindex", command.Columns...), "`") + } + return fmt.Sprintf( + "CREATE SPATIAL INDEX %s ON %s (%s)", + index, + g.WrapTable(bp.Name, "`"), + g.WrapColumnize(command.Columns, "`"), + ), nil +} + +func (g *mysqlGrammar) CompileDropSpatialIndex(bp *blueprint.Blueprint, command *blueprint.Command) (string, error) { + return g.CompileDropIndex(bp, command) +} + +func (g *mysqlGrammar) CompileTableComment(bp *blueprint.Blueprint, command *blueprint.Command) (string, error) { + if bp.IsCreating() { + return "", nil + } + return fmt.Sprintf( + "ALTER TABLE %s COMMENT = %s", + g.WrapTable(bp.Name, "`"), + g.QuoteString(strings.ReplaceAll(command.Comment, "'", "''")), + ), nil +} + +func (g *mysqlGrammar) CompileAutoIncrementStartingValues( + bp *blueprint.Blueprint, command *blueprint.Command, +) (string, error) { + if command.Value <= 0 { + return "", nil + } + return fmt.Sprintf( + "ALTER TABLE %s AUTO_INCREMENT = %d", + g.WrapTable(bp.Name, "`"), + command.Value, + ), nil +} + +func (g *mysqlGrammar) CompileRename(bp *blueprint.Blueprint, command *blueprint.Command) (string, error) { + return fmt.Sprintf( + "RENAME TABLE %s TO %s", + g.WrapTable(bp.Name, "`"), + g.WrapTable(command.To, "`"), + ), nil +} + func (g *mysqlGrammar) GetFluentCommands() []func(*blueprint.Blueprint, *blueprint.Command) string { return []func(*blueprint.Blueprint, *blueprint.Command) string{} } diff --git a/schema/grammars/mysql_grammar_test.go b/schema/grammars/mysql_grammar_test.go index 9028061..553760b 100644 --- a/schema/grammars/mysql_grammar_test.go +++ b/schema/grammars/mysql_grammar_test.go @@ -231,7 +231,7 @@ func TestMysqlGrammar_CompileAdd(t *testing.T) { blueprint: func(table *blueprint.Blueprint) { table.String("full_name").VirtualAs("concat(first_name, ' ', last_name)") }, - want: "ALTER TABLE `users` ADD COLUMN `full_name` VARCHAR(255) GENERATED ALWAYS AS (concat(first_name, ' ', last_name)) VIRTUAL NOT NULL", + want: "ALTER TABLE `users` ADD COLUMN `full_name` VARCHAR(255) GENERATED ALWAYS AS (concat(first_name, ' ', last_name)) VIRTUAL", wantErr: false, }, { @@ -240,7 +240,7 @@ func TestMysqlGrammar_CompileAdd(t *testing.T) { blueprint: func(table *blueprint.Blueprint) { table.String("full_name").StoredAs("concat(first_name, ' ', last_name)") }, - want: "ALTER TABLE `users` ADD COLUMN `full_name` VARCHAR(255) GENERATED ALWAYS AS (concat(first_name, ' ', last_name)) STORED NOT NULL", + want: "ALTER TABLE `users` ADD COLUMN `full_name` VARCHAR(255) GENERATED ALWAYS AS (concat(first_name, ' ', last_name)) STORED", wantErr: false, }, { @@ -434,21 +434,21 @@ func TestMysqlGrammar_CompileRename(t *testing.T) { name: "rename table with valid names", table: "users", newName: "customers", - want: "ALTER TABLE `users` RENAME TO `customers`", + want: "RENAME TABLE `users` TO `customers`", wantErr: false, }, { name: "rename table with underscore names", table: "old_table_name", newName: "new_table_name", - want: "ALTER TABLE `old_table_name` RENAME TO `new_table_name`", + want: "RENAME TABLE `old_table_name` TO `new_table_name`", wantErr: false, }, { name: "rename table with numeric names", table: "table1", newName: "table2", - want: "ALTER TABLE `table1` RENAME TO `table2`", + want: "RENAME TABLE `table1` TO `table2`", wantErr: false, }, } @@ -1895,3 +1895,310 @@ func TestMysqlGrammar_GetType(t *testing.T) { }) } } + +func TestMysqlGrammar_CompileCreate_Temporary(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "temp_logs", TemporaryVal: true} + bp.String("message") + sql, err := g.CompileCreate(bp) + require.NoError(t, err) + assert.Contains(t, sql, "CREATE TEMPORARY TABLE") + assert.Contains(t, sql, "`temp_logs`") +} + +func TestMysqlGrammar_CompileSpatialIndex(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + tests := []struct { + name string + setup func(*blueprint.Blueprint) + want string + wantErr bool + }{ + { + name: "basic spatial index", + setup: func(bp *blueprint.Blueprint) { + bp.SpatialIndex("location") + }, + want: "CREATE SPATIAL INDEX `locations_location_spatialindex` ON `locations` (`location`)", + }, + { + name: "spatial index with custom name", + setup: func(bp *blueprint.Blueprint) { + bp.SpatialIndex("location").Name("idx_loc") + }, + want: "CREATE SPATIAL INDEX `idx_loc` ON `locations` (`location`)", + }, + { + name: "spatial index with empty column", + setup: func(bp *blueprint.Blueprint) { bp.SpatialIndex("") }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "locations"} + tt.setup(bp) + got, err := g.CompileSpatialIndex(bp, bp.Commands[0]) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestMysqlGrammar_CompileDropSpatialIndex(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "locations"} + bp.DropSpatialIndex("idx_loc") + got, err := g.CompileDropSpatialIndex(bp, bp.Commands[0]) + require.NoError(t, err) + assert.Equal(t, "ALTER TABLE `locations` DROP INDEX `idx_loc`", got) +} + +func TestMysqlGrammar_CompileTableComment(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + t.Run("alter table comment", func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "users"} + bp.Comment("User accounts") + got, err := g.CompileTableComment(bp, bp.Commands[0]) + require.NoError(t, err) + assert.Equal(t, "ALTER TABLE `users` COMMENT = 'User accounts'", got) + }) + + t.Run("skip comment during create", func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "users"} + bp.Create() + bp.Comment("User accounts") + // find tableComment command + var cmd *blueprint.Command + for _, c := range bp.Commands { + if c.Name == blueprint.CommandTableComment { + cmd = c + } + } + require.NotNil(t, cmd) + got, err := g.CompileTableComment(bp, cmd) + require.NoError(t, err) + assert.Empty(t, got) + }) +} + +func TestMysqlGrammar_CompileAutoIncrementStartingValues(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + t.Run("sets auto increment start", func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "orders"} + bp.AutoIncrementStartingValues(1000) + var cmd *blueprint.Command + for _, c := range bp.Commands { + if c.Name == blueprint.CommandAutoIncrementStartingValues { + cmd = c + } + } + require.NotNil(t, cmd) + got, err := g.CompileAutoIncrementStartingValues(bp, cmd) + require.NoError(t, err) + assert.Equal(t, "ALTER TABLE `orders` AUTO_INCREMENT = 1000", got) + }) + + t.Run("zero value produces empty string", func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "orders"} + cmd := &blueprint.Command{Name: blueprint.CommandAutoIncrementStartingValues, Value: 0} + got, err := g.CompileAutoIncrementStartingValues(bp, cmd) + require.NoError(t, err) + assert.Empty(t, got) + }) +} + +func TestMysqlGrammar_GetType_NewTypes(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + tests := []struct { + name string + blueprint func(*blueprint.Blueprint) + want string + }{ + { + name: "binary without length returns BLOB", + blueprint: func(bp *blueprint.Blueprint) { + bp.Binary("data") + }, + want: "BLOB", + }, + { + name: "binary with length returns VARBINARY", + blueprint: func(bp *blueprint.Blueprint) { + bp.Binary("data", 255) + }, + want: "VARBINARY(255)", + }, + { + name: "binary with length and Fixed returns BINARY", + blueprint: func(bp *blueprint.Blueprint) { + bp.Binary("hash", 32).Fixed() + }, + want: "BINARY(32)", + }, + { + name: "float without explicit precision defaults to FLOAT(53)", + blueprint: func(bp *blueprint.Blueprint) { + bp.Float("score") + }, + want: "FLOAT(53)", + }, + { + name: "float with precision returns FLOAT(n)", + blueprint: func(bp *blueprint.Blueprint) { + bp.Float("score", 6) + }, + want: "FLOAT(6)", + }, + { + name: "float with zero precision returns bare FLOAT", + blueprint: func(bp *blueprint.Blueprint) { + bp.Float("score", 0) + }, + want: "FLOAT", + }, + { + name: "vector without dimensions returns VECTOR", + blueprint: func(bp *blueprint.Blueprint) { + bp.Vector("embedding") + }, + want: "VECTOR", + }, + { + name: "vector with dimensions returns VECTOR(n)", + blueprint: func(bp *blueprint.Blueprint) { + bp.Vector("embedding", 1536) + }, + want: "VECTOR(1536)", + }, + { + name: "raw column returns definition", + blueprint: func(bp *blueprint.Blueprint) { + bp.RawColumn("payload", "MEDIUMBLOB NOT NULL") + }, + want: "MEDIUMBLOB NOT NULL", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "t"} + tt.blueprint(bp) + got := g.GetType(bp.Columns[0]) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestMysqlGrammar_CompileAdd_Modifiers(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + tests := []struct { + name string + table string + blueprint func(*blueprint.Blueprint) + want string + wantErr bool + }{ + { + name: "on update current timestamp", + table: "users", + blueprint: func(table *blueprint.Blueprint) { + table.Timestamp("updated_at").UseCurrentOnUpdate() + }, + want: "ALTER TABLE `users` ADD COLUMN `updated_at` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP", + }, + { + name: "column charset", + table: "users", + blueprint: func(table *blueprint.Blueprint) { + table.String("name", 255).Charset("utf8mb4") + }, + want: "ALTER TABLE `users` ADD COLUMN `name` VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL", + }, + { + name: "column collation", + table: "users", + blueprint: func(table *blueprint.Blueprint) { + table.String("name", 255).Collation("utf8mb4_unicode_ci") + }, + want: "ALTER TABLE `users` ADD COLUMN `name` VARCHAR(255) COLLATE utf8mb4_unicode_ci NOT NULL", + }, + { + name: "column charset and collation", + table: "users", + blueprint: func(table *blueprint.Blueprint) { + table.String("bio", 500).Charset("utf8mb4").Collation("utf8mb4_unicode_ci") + }, + want: "ALTER TABLE `users` ADD COLUMN `bio` VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL", + }, + { + name: "invisible column", + table: "users", + blueprint: func(table *blueprint.Blueprint) { + table.String("secret", 100).Invisible() + }, + want: "ALTER TABLE `users` ADD COLUMN `secret` VARCHAR(100) NOT NULL INVISIBLE", + }, + { + name: "raw column definition", + table: "events", + blueprint: func(table *blueprint.Blueprint) { + table.RawColumn("payload", "MEDIUMBLOB") + }, + want: "ALTER TABLE `events` ADD COLUMN `payload` MEDIUMBLOB NOT NULL", + }, + { + name: "invisible nullable column", + table: "users", + blueprint: func(table *blueprint.Blueprint) { + table.String("token", 255).Nullable().Invisible() + }, + want: "ALTER TABLE `users` ADD COLUMN `token` VARCHAR(255) NULL INVISIBLE", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bp := &blueprint.Blueprint{Name: tt.table} + tt.blueprint(bp) + got, err := g.CompileAdd(bp) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestMysqlGrammar_CompileCreate_RawColumn(t *testing.T) { + g, err := grammars.NewGrammar("mysql") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "events"} + bp.RawColumn("payload", "MEDIUMBLOB") + bp.String("source", 100) + sql, err := g.CompileCreate(bp) + require.NoError(t, err) + assert.Equal(t, "CREATE TABLE `events` (`payload` MEDIUMBLOB NOT NULL, `source` VARCHAR(100) NOT NULL)", sql) +} diff --git a/schema/grammars/postgres_grammar.go b/schema/grammars/postgres_grammar.go index ef02418..e738b7e 100644 --- a/schema/grammars/postgres_grammar.go +++ b/schema/grammars/postgres_grammar.go @@ -103,10 +103,46 @@ func (g *postgresGrammar) CompileCreate(bp *blueprint.Blueprint) (string, error) } columns = append(columns, g.getConstraints(bp)...) - sql := fmt.Sprintf("CREATE TABLE %s (%s)", g.WrapTable(bp.Name, `"`), strings.Join(columns, ", ")) + create := "CREATE TABLE" + if bp.TemporaryVal { + create = "CREATE TEMPORARY TABLE" + } + sql := fmt.Sprintf("%s %s (%s)", create, g.WrapTable(bp.Name, `"`), strings.Join(columns, ", ")) return sql, nil } +func (g *postgresGrammar) CompileVectorIndex(bp *blueprint.Blueprint, command *blueprint.Command) (string, error) { + if len(command.Columns) == 0 || slices.Contains(command.Columns, "") { + return "", errors.New("vector index columns cannot be empty") + } + index := command.Index + if index == "" { + index = g.CreateIndexName(bp, "vectorindex", command.Columns...) + } + operatorClass := command.OperatorClass + if operatorClass == "" { + operatorClass = "vector_cosine_ops" + } + return fmt.Sprintf( + `CREATE INDEX %s ON %s USING hnsw (%s %s)`, + g.WrapIndexName(index, `"`), + g.WrapTable(bp.Name, `"`), + g.WrapColumnize(command.Columns, `"`), + operatorClass, + ), nil +} + +func (g *postgresGrammar) CompileTableComment(bp *blueprint.Blueprint, command *blueprint.Command) (string, error) { + if bp.IsCreating() { + return "", nil + } + return fmt.Sprintf( + `COMMENT ON TABLE %s IS %s`, + g.WrapTable(bp.Name, `"`), + g.QuoteString(command.Comment), + ), nil +} + func (g *postgresGrammar) CompileAdd(bp *blueprint.Blueprint) (string, error) { columns, err := g.getColumns(bp) if err != nil { @@ -495,6 +531,7 @@ func (g *postgresGrammar) GetType(col *blueprint.Column) string { blueprint.ColumnTypeGeography: g.typeGeography, blueprint.ColumnTypeGeometry: g.typeGeometry, blueprint.ColumnTypePoint: g.typePoint, + blueprint.ColumnTypeRaw: g.typeRaw, } if fn, ok := typeMapFunc[col.ColumnType]; ok { return fn(col) @@ -502,6 +539,13 @@ func (g *postgresGrammar) GetType(col *blueprint.Column) string { return col.ColumnType } +func (g *postgresGrammar) typeRaw(col *blueprint.Column) string { + if col.RawDefinition != nil { + return *col.RawDefinition + } + return "" +} + func (g *postgresGrammar) typeChar(col *blueprint.Column) string { return fmt.Sprintf("CHAR(%d)", *col.Length) } diff --git a/schema/grammars/postgres_grammar_test.go b/schema/grammars/postgres_grammar_test.go index a7864eb..01f32b0 100644 --- a/schema/grammars/postgres_grammar_test.go +++ b/schema/grammars/postgres_grammar_test.go @@ -1829,3 +1829,100 @@ func TestPgGrammar_GetType(t *testing.T) { }) } } + +func TestPgGrammar_CompileCreate_Temporary(t *testing.T) { + g, err := grammars.NewGrammar("postgres") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "temp_logs", TemporaryVal: true} + bp.String("message") + sql, err := g.CompileCreate(bp) + require.NoError(t, err) + assert.Contains(t, sql, "CREATE TEMPORARY TABLE") + assert.Contains(t, sql, `"temp_logs"`) +} + +func TestPgGrammar_CompileVectorIndex(t *testing.T) { + g, err := grammars.NewGrammar("postgres") + require.NoError(t, err) + + tests := []struct { + name string + setup func(*blueprint.Blueprint) + want string + wantErr bool + }{ + { + name: "basic vector index uses cosine ops", + setup: func(bp *blueprint.Blueprint) { + bp.VectorIndex("embedding") + }, + want: `CREATE INDEX "items_embedding_vectorindex" ON "items" USING hnsw ("embedding" vector_cosine_ops)`, + }, + { + name: "vector index with custom name", + setup: func(bp *blueprint.Blueprint) { + bp.VectorIndex("embedding").Name("idx_emb") + }, + want: `CREATE INDEX "idx_emb" ON "items" USING hnsw ("embedding" vector_cosine_ops)`, + }, + { + name: "empty column returns error", + setup: func(bp *blueprint.Blueprint) { bp.VectorIndex("") }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "items"} + tt.setup(bp) + got, err := g.CompileVectorIndex(bp, bp.Commands[0]) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestPgGrammar_CompileTableComment(t *testing.T) { + g, err := grammars.NewGrammar("postgres") + require.NoError(t, err) + + t.Run("alter table comment", func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "users"} + bp.Comment("User accounts") + got, err := g.CompileTableComment(bp, bp.Commands[0]) + require.NoError(t, err) + assert.Equal(t, `COMMENT ON TABLE "users" IS 'User accounts'`, got) + }) + + t.Run("skip comment during create", func(t *testing.T) { + bp := &blueprint.Blueprint{Name: "users"} + bp.Create() + bp.Comment("User accounts") + var cmd *blueprint.Command + for _, c := range bp.Commands { + if c.Name == blueprint.CommandTableComment { + cmd = c + } + } + require.NotNil(t, cmd) + got, err := g.CompileTableComment(bp, cmd) + require.NoError(t, err) + assert.Empty(t, got) + }) +} + +func TestPgGrammar_GetType_RawColumn(t *testing.T) { + g, err := grammars.NewGrammar("postgres") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "t"} + bp.RawColumn("payload", "BYTEA NOT NULL") + got := g.GetType(bp.Columns[0]) + assert.Equal(t, "BYTEA NOT NULL", got) +} diff --git a/schema/grammars/sqlite_grammar.go b/schema/grammars/sqlite_grammar.go index 72cefa9..bc24bb1 100644 --- a/schema/grammars/sqlite_grammar.go +++ b/schema/grammars/sqlite_grammar.go @@ -37,7 +37,11 @@ func (g *sqliteGrammar) CompileCreate(bp *blueprint.Blueprint) (string, error) { if err != nil { return "", err } - sql := fmt.Sprintf("CREATE TABLE %q (%s)", bp.Name, strings.Join(columns, ", ")) + create := "CREATE TABLE" + if bp.TemporaryVal { + create = "CREATE TEMPORARY TABLE" + } + sql := fmt.Sprintf("%s %q (%s)", create, bp.Name, strings.Join(columns, ", ")) return sql, nil } @@ -147,6 +151,7 @@ func (g *sqliteGrammar) GetType(col *blueprint.Column) string { blueprint.ColumnTypeGeometry: g.typeGeometry, blueprint.ColumnTypeGeography: g.typeGeography, blueprint.ColumnTypePoint: g.typePoint, + blueprint.ColumnTypeRaw: g.typeRaw, } if fn, ok := typeFuncMap[col.ColumnType]; ok { return fn(col) @@ -154,6 +159,13 @@ func (g *sqliteGrammar) GetType(col *blueprint.Column) string { return col.ColumnType } +func (g *sqliteGrammar) typeRaw(col *blueprint.Column) string { + if col.RawDefinition != nil { + return *col.RawDefinition + } + return "" +} + func (g *sqliteGrammar) typeChar(_ *blueprint.Column) string { return "TEXT" } diff --git a/schema/grammars/sqlite_grammar_test.go b/schema/grammars/sqlite_grammar_test.go index 9d712cb..c3ec1c0 100644 --- a/schema/grammars/sqlite_grammar_test.go +++ b/schema/grammars/sqlite_grammar_test.go @@ -710,3 +710,25 @@ func TestSqliteGrammar_GetType(t *testing.T) { }) } } + +func TestSqliteGrammar_CompileCreate_Temporary(t *testing.T) { + g, err := grammars.NewGrammar("sqlite") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "temp_logs", TemporaryVal: true} + bp.String("message") + sql, err := g.CompileCreate(bp) + require.NoError(t, err) + assert.Contains(t, sql, "CREATE TEMPORARY TABLE") + assert.Contains(t, sql, `"temp_logs"`) +} + +func TestSqliteGrammar_GetType_RawColumn(t *testing.T) { + g, err := grammars.NewGrammar("sqlite") + require.NoError(t, err) + + bp := &blueprint.Blueprint{Name: "t"} + bp.RawColumn("payload", "BLOB NOT NULL") + got := g.GetType(bp.Columns[0]) + assert.Equal(t, "BLOB NOT NULL", got) +} From 16d8a20ad00ba0edbe9b4bf492b3739b4d7b6fbf Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 22:58:08 +0700 Subject: [PATCH 2/7] test(builders): replace db-backed suites with unit tests --- schema/builders/builder_unit_test.go | 562 ++++++++++++++++++++ schema/builders/mariadb_builder_test.go | 117 ----- schema/builders/mysql_builder_test.go | 639 ----------------------- schema/builders/postgres_builder_test.go | 634 ---------------------- schema/builders/sqlite_builder_test.go | 574 -------------------- 5 files changed, 562 insertions(+), 1964 deletions(-) create mode 100644 schema/builders/builder_unit_test.go delete mode 100644 schema/builders/mariadb_builder_test.go delete mode 100644 schema/builders/mysql_builder_test.go delete mode 100644 schema/builders/postgres_builder_test.go delete mode 100644 schema/builders/sqlite_builder_test.go diff --git a/schema/builders/builder_unit_test.go b/schema/builders/builder_unit_test.go new file mode 100644 index 0000000..5ad1d7f --- /dev/null +++ b/schema/builders/builder_unit_test.go @@ -0,0 +1,562 @@ +package builders_test + +import ( + "database/sql" + "errors" + "strings" + "testing" + + "github.com/akfaiz/migris/schema" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockContext struct { + dialect string + + execs []string + queries []string + queryRow []string + + queryResults map[string]queryResult + row schema.Row +} + +type queryResult struct { + rows schema.Rows + err error +} + +func newMockContext(dialect string) *mockContext { + return &mockContext{ + dialect: dialect, + queryResults: make(map[string]queryResult), + row: &mockRow{values: []any{1}}, + } +} + +func (m *mockContext) Exec(query string, _ ...any) (sql.Result, error) { + m.execs = append(m.execs, strings.TrimSpace(query)) + return mockResult{}, nil +} + +func (m *mockContext) Query(query string, _ ...any) (schema.Rows, error) { + query = strings.TrimSpace(query) + m.queries = append(m.queries, query) + if result, ok := m.queryResults[query]; ok { + return result.rows, result.err + } + return &mockRows{}, nil +} + +func (m *mockContext) QueryRow(query string, _ ...any) schema.Row { + query = strings.TrimSpace(query) + m.queryRow = append(m.queryRow, query) + return m.row +} + +func (m *mockContext) Dialect() string { + return m.dialect +} + +func (m *mockContext) withRows(query string, columns []string, rows ...[]any) { + m.queryResults[strings.TrimSpace(query)] = queryResult{ + rows: &mockRows{columns: columns, rows: rows}, + } +} + +type mockRows struct { + columns []string + rows [][]any + idx int + closed bool + err error +} + +func (m *mockRows) Next() bool { + return m.idx < len(m.rows) +} + +func (m *mockRows) Scan(dest ...any) error { + if m.idx >= len(m.rows) { + return errors.New("scan without row") + } + row := m.rows[m.idx] + m.idx++ + if len(dest) != len(row) { + return errors.New("scan destination count mismatch") + } + return assignValues(dest, row) +} + +func (m *mockRows) Close() error { + m.closed = true + return nil +} + +func (m *mockRows) Err() error { + return m.err +} + +func (m *mockRows) Columns() ([]string, error) { + return m.columns, nil +} + +type mockRow struct { + values []any + err error +} + +func (m *mockRow) Scan(dest ...any) error { + if m.err != nil { + return m.err + } + if len(dest) != len(m.values) { + return errors.New("scan destination count mismatch") + } + return assignValues(dest, m.values) +} + +type mockResult struct{} + +func (mockResult) LastInsertId() (int64, error) { return 0, nil } +func (mockResult) RowsAffected() (int64, error) { return 0, nil } + +func assignValues(dest []any, values []any) error { + for i := range dest { + switch d := dest[i].(type) { + case *any: + *d = values[i] + case *string: + v, ok := values[i].(string) + if !ok { + return errors.New("expected string value") + } + *d = v + case *int: + v, ok := values[i].(int) + if !ok { + return errors.New("expected int value") + } + *d = v + case *bool: + v, ok := values[i].(bool) + if !ok { + return errors.New("expected bool value") + } + *d = v + case *sql.NullString: + switch v := values[i].(type) { + case sql.NullString: + *d = v + case string: + *d = sql.NullString{String: v, Valid: true} + case nil: + *d = sql.NullString{} + default: + return errors.New("expected null string value") + } + default: + return errors.New("unsupported scan destination") + } + } + return nil +} + +func TestBuilderInputValidation(t *testing.T) { + for _, dialect := range []string{"sqlite3", "mysql", "mariadb", "postgres"} { + t.Run(dialect, func(t *testing.T) { + builder, err := schema.NewBuilder(dialect) + require.NoError(t, err) + ctx := newMockContext(dialect) + + require.Error(t, builder.Create(nil, "users", func(*schema.Blueprint) {})) + require.Error(t, builder.Create(ctx, "", func(*schema.Blueprint) {})) + require.Error(t, builder.Create(ctx, "users", nil)) + require.Error(t, builder.Table(nil, "users", func(*schema.Blueprint) {})) + require.Error(t, builder.Table(ctx, "", func(*schema.Blueprint) {})) + require.Error(t, builder.Table(ctx, "users", nil)) + require.Error(t, builder.Drop(nil, "users")) + require.Error(t, builder.Drop(ctx, "")) + require.Error(t, builder.DropIfExists(nil, "users")) + require.Error(t, builder.DropIfExists(ctx, "")) + require.Error(t, builder.Rename(nil, "users", "accounts")) + require.Error(t, builder.Rename(ctx, "", "accounts")) + require.Error(t, builder.Rename(ctx, "users", "")) + require.Error(t, errOnly(builder.HasTable(nil, "users"))) + require.Error(t, errOnly(builder.HasTable(ctx, ""))) + require.Error(t, errOnly(builder.HasColumn(nil, "users", "id"))) + require.Error(t, errOnly(builder.HasColumn(ctx, "", "id"))) + require.Error(t, errOnly(builder.HasColumn(ctx, "users", ""))) + require.Error(t, errOnly(builder.HasColumns(nil, "users", []string{"id"}))) + require.Error(t, errOnly(builder.HasColumns(ctx, "", []string{"id"}))) + require.Error(t, errOnly(builder.HasColumns(ctx, "users", nil))) + require.Error(t, errOnly(builder.HasIndex(nil, "users", []string{"id"}))) + require.Error(t, errOnly(builder.HasIndex(ctx, "", []string{"id"}))) + require.Error(t, errOnly(builder.HasIndex(ctx, "users", nil))) + require.Error(t, errOnly(builder.GetColumns(nil, "users"))) + require.Error(t, errOnly(builder.GetColumns(ctx, ""))) + require.Error(t, errOnly(builder.GetIndexes(nil, "users"))) + require.Error(t, errOnly(builder.GetIndexes(ctx, ""))) + require.Error(t, errOnly(builder.GetTables(nil))) + }) + } +} + +func TestBuilderExecutesCompiledStatements(t *testing.T) { + tests := []struct { + dialect string + fragments []string + }{ + { + dialect: "sqlite3", + fragments: []string{ + "CREATE TABLE \"users\"", + "ALTER TABLE \"users\" ADD COLUMN \"email\"", + "DROP TABLE \"users\"", + "DROP TABLE IF EXISTS \"users\"", + "ALTER TABLE \"users\" RENAME TO \"accounts\"", + }, + }, + { + dialect: "mysql", + fragments: []string{ + "CREATE TABLE `users`", + "ALTER TABLE `users` ADD COLUMN `email`", + "DROP TABLE `users`", + "DROP TABLE IF EXISTS `users`", + "RENAME TABLE `users` TO `accounts`", + }, + }, + { + dialect: "mariadb", + fragments: []string{ + "CREATE TABLE `users`", + "ALTER TABLE `users` ADD COLUMN `email`", + "DROP TABLE `users`", + "DROP TABLE IF EXISTS `users`", + "RENAME TABLE `users` TO `accounts`", + }, + }, + { + dialect: "postgres", + fragments: []string{ + "CREATE TABLE \"users\"", + "ALTER TABLE \"users\" ADD COLUMN \"email\"", + "DROP TABLE \"users\"", + "DROP TABLE IF EXISTS \"users\"", + "ALTER TABLE \"users\" RENAME TO \"accounts\"", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.dialect, func(t *testing.T) { + builder, err := schema.NewBuilder(tt.dialect) + require.NoError(t, err) + ctx := newMockContext(tt.dialect) + + require.NoError(t, builder.Create(ctx, "users", func(table *schema.Blueprint) { + table.ID() + table.String("name", 100) + })) + require.NoError(t, builder.Table(ctx, "users", func(table *schema.Blueprint) { + table.String("email", 255).Nullable() + })) + require.NoError(t, builder.Drop(ctx, "users")) + require.NoError(t, builder.DropIfExists(ctx, "users")) + require.NoError(t, builder.Rename(ctx, "users", "accounts")) + + require.Len(t, ctx.execs, len(tt.fragments)) + for i, fragment := range tt.fragments { + assert.Contains(t, ctx.execs[i], fragment) + } + }) + } +} + +func TestBuilderGetColumnsMapsRows(t *testing.T) { + tests := []struct { + dialect string + query string + columns []string + rows [][]any + }{ + { + dialect: "sqlite3", + query: compiledColumnsQuery("sqlite3"), + rows: [][]any{ + {0, "id", "INTEGER", 1, nil, 1}, + {1, "email", "varchar(255)", 0, sql.NullString{}, 0}, + }, + }, + { + dialect: "mysql", + query: compiledColumnsQuery("mysql"), + columns: []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}, + rows: [][]any{ + {"id", "bigint unsigned", nil, "NO", "PRI", nil, "auto_increment", "", ""}, + {"email", "varchar(255)", "utf8mb4_unicode_ci", "YES", "UNI", nil, "", "", "user email"}, + }, + }, + { + dialect: "mariadb", + query: compiledColumnsQuery("mariadb"), + columns: []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}, + rows: [][]any{ + {"guid", "uuid", nil, "NO", "", nil, "", "", ""}, + }, + }, + { + dialect: "postgres", + query: compiledColumnsQuery("postgres"), + rows: [][]any{ + {"id", "bigint", "NO", "nextval('users_id_seq'::regclass)", nil}, + {"email", "character varying", "YES", nil, "user email"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.dialect, func(t *testing.T) { + builder, err := schema.NewBuilder(tt.dialect) + require.NoError(t, err) + ctx := newMockContext(tt.dialect) + ctx.withRows(tt.query, tt.columns, tt.rows...) + + columns, err := builder.GetColumns(ctx, "users") + require.NoError(t, err) + require.Len(t, columns, len(tt.rows)) + if tt.dialect == "mysql" { + assert.Equal(t, "bigint unsigned", columns[0].TypeFull) + assert.Equal(t, "bigint unsigned", columns[0].TypeName) + assert.True(t, columns[1].Nullable) + assert.Equal(t, "varchar", columns[1].TypeName) + assert.Equal(t, "user email", columns[1].Comment.String) + } + if tt.dialect == "mariadb" { + assert.Equal(t, "uuid", columns[0].TypeName) + } + if tt.dialect == "postgres" { + assert.Equal(t, "bigint", columns[0].TypeName) + assert.False(t, columns[0].Nullable) + assert.True(t, columns[1].Nullable) + assert.Equal(t, "user email", columns[1].Comment.String) + } + if tt.dialect == "sqlite3" { + assert.Equal(t, "INTEGER", columns[0].TypeName) + assert.False(t, columns[0].Nullable) + assert.True(t, columns[1].Nullable) + } + }) + } +} + +func TestBuilderGetColumnsReturnsNilForMissingTable(t *testing.T) { + for _, dialect := range []string{"sqlite3", "mysql", "mariadb", "postgres"} { + t.Run(dialect, func(t *testing.T) { + builder, err := schema.NewBuilder(dialect) + require.NoError(t, err) + ctx := newMockContext(dialect) + ctx.row = &mockRow{err: sql.ErrNoRows} + + columns, err := builder.GetColumns(ctx, "missing") + require.NoError(t, err) + assert.Nil(t, columns) + assert.Empty(t, ctx.queries) + }) + } +} + +func TestBuilderGetIndexesMapsRows(t *testing.T) { + t.Run("sqlite3", func(t *testing.T) { + builder, err := schema.NewBuilder("sqlite3") + require.NoError(t, err) + ctx := newMockContext("sqlite3") + ctx.withRows(`PRAGMA index_list("orders")`, nil, + []any{0, "idx_orders_company_user", 0, "c", 0}, + []any{1, "sqlite_autoindex_orders_1", 1, "pk", 0}, + ) + ctx.withRows(`PRAGMA index_info("idx_orders_company_user")`, nil, + []any{0, 1, "company_id"}, + []any{1, 2, "user_id"}, + ) + ctx.withRows(`PRAGMA index_info("sqlite_autoindex_orders_1")`, nil, + []any{0, 0, "id"}, + ) + + indexes, err := builder.GetIndexes(ctx, "orders") + require.NoError(t, err) + require.Len(t, indexes, 2) + assert.Equal(t, []string{"company_id", "user_id"}, indexes[0].Columns) + assert.False(t, indexes[0].Unique) + assert.True(t, indexes[1].Primary) + }) + + for _, dialect := range []string{"mysql", "mariadb"} { + t.Run(dialect, func(t *testing.T) { + builder, err := schema.NewBuilder(dialect) + require.NoError(t, err) + ctx := newMockContext(dialect) + ctx.withRows(compiledIndexesQuery(dialect, "orders"), []string{"Non_unique", "Key_name", "Column_name"}, + []any{0, "PRIMARY", "id"}, + []any{1, "idx_orders_company_user", "company_id"}, + []any{1, "idx_orders_company_user", "user_id"}, + ) + + indexes, err := builder.GetIndexes(ctx, "orders") + require.NoError(t, err) + require.Len(t, indexes, 2) + assert.True(t, indexes[0].Primary) + assert.Equal(t, []string{"company_id", "user_id"}, indexes[1].Columns) + assert.False(t, indexes[1].Unique) + }) + } + + t.Run("postgres", func(t *testing.T) { + builder, err := schema.NewBuilder("postgres") + require.NoError(t, err) + ctx := newMockContext("postgres") + ctx.withRows(compiledIndexesQuery("postgres", "orders"), nil, + []any{"orders_pkey", true, true, "id"}, + []any{"idx_orders_company_user", false, false, "company_id"}, + []any{"idx_orders_company_user", false, false, "user_id"}, + ) + + indexes, err := builder.GetIndexes(ctx, "orders") + require.NoError(t, err) + require.Len(t, indexes, 2) + assert.True(t, indexes[0].Primary) + assert.Equal(t, []string{"company_id", "user_id"}, indexes[1].Columns) + assert.False(t, indexes[1].Unique) + }) +} + +func TestBuilderGetTablesMapsRows(t *testing.T) { + tests := []struct { + dialect string + query string + rows [][]any + assert func(*testing.T, []*schema.TableInfo) + }{ + { + dialect: "sqlite3", + query: compiledTablesQuery("sqlite3"), + rows: [][]any{{"users"}}, + assert: func(t *testing.T, tables []*schema.TableInfo) { + assert.Equal(t, "users", tables[0].Name) + }, + }, + { + dialect: "mysql", + query: compiledTablesQuery("mysql"), + rows: [][]any{{"users", "application users"}}, + assert: func(t *testing.T, tables []*schema.TableInfo) { + assert.Equal(t, "users", tables[0].Name) + assert.Equal(t, "application users", tables[0].Comment.String) + }, + }, + { + dialect: "mariadb", + query: compiledTablesQuery("mariadb"), + rows: [][]any{{"users", "application users"}}, + assert: func(t *testing.T, tables []*schema.TableInfo) { + assert.Equal(t, "users", tables[0].Name) + assert.Equal(t, "application users", tables[0].Comment.String) + }, + }, + { + dialect: "postgres", + query: compiledTablesQuery("postgres"), + rows: [][]any{{"users", "public", "application users"}}, + assert: func(t *testing.T, tables []*schema.TableInfo) { + assert.Equal(t, "users", tables[0].Name) + assert.Equal(t, "public", tables[0].Schema) + assert.Equal(t, "application users", tables[0].Comment.String) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.dialect, func(t *testing.T) { + builder, err := schema.NewBuilder(tt.dialect) + require.NoError(t, err) + ctx := newMockContext(tt.dialect) + ctx.withRows(tt.query, nil, tt.rows...) + + tables, err := builder.GetTables(ctx) + require.NoError(t, err) + require.Len(t, tables, len(tt.rows)) + tt.assert(t, tables) + }) + } +} + +func TestBuilderHasHelpersUseMappedMetadata(t *testing.T) { + builder, err := schema.NewBuilder("postgres") + require.NoError(t, err) + ctx := newMockContext("postgres") + ctx.withRows(compiledColumnsQuery("postgres"), nil, + []any{"id", "bigint", "NO", nil, nil}, + []any{"email", "character varying", "YES", nil, nil}, + ) + ctx.withRows(compiledIndexesQuery("postgres", "users"), nil, + []any{"idx_users_email", false, false, "email"}, + ) + + exists, err := builder.HasColumn(ctx, "users", "email") + require.NoError(t, err) + assert.True(t, exists) + + exists, err = builder.HasColumns(ctx, "users", []string{"id", "missing"}) + require.NoError(t, err) + assert.False(t, exists) + + exists, err = builder.HasIndex(ctx, "users", []string{"idx_users_email"}) + require.NoError(t, err) + assert.True(t, exists) + + exists, err = builder.HasTable(ctx, "users") + require.NoError(t, err) + assert.True(t, exists) +} + +func errOnly(_ any, err error) error { + return err +} + +func compiledColumnsQuery(dialect string) string { + grammar, err := schema.NewGrammar(dialect) + if err != nil { + panic(err) + } + query, err := grammar.CompileColumns("", "users") + if err != nil { + panic(err) + } + return strings.TrimSpace(query) +} + +func compiledIndexesQuery(dialect, table string) string { + grammar, err := schema.NewGrammar(dialect) + if err != nil { + panic(err) + } + query, err := grammar.CompileIndexes("", table) + if err != nil { + panic(err) + } + return strings.TrimSpace(query) +} + +func compiledTablesQuery(dialect string) string { + grammar, err := schema.NewGrammar(dialect) + if err != nil { + panic(err) + } + query, err := grammar.CompileTables("") + if err != nil { + panic(err) + } + return strings.TrimSpace(query) +} diff --git a/schema/builders/mariadb_builder_test.go b/schema/builders/mariadb_builder_test.go deleted file mode 100644 index af2ed2e..0000000 --- a/schema/builders/mariadb_builder_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package builders_test - -import ( - "context" - "database/sql" - "testing" - - "github.com/akfaiz/migris/internal/testutil" - "github.com/akfaiz/migris/schema" - "github.com/stretchr/testify/suite" - "github.com/testcontainers/testcontainers-go" -) - -func TestMariadbBuilderSuite(t *testing.T) { - suite.Run(t, new(mariadbBuilderSuite)) -} - -type mariadbBuilderSuite struct { - suite.Suite - - ctx context.Context - db *sql.DB - builder schema.Builder - tc testcontainers.Container -} - -func (s *mariadbBuilderSuite) SetupSuite() { - s.ctx = context.Background() - - container, db, err := testutil.StartMariaDBTestDB(s.ctx) - s.Require().NoError(err) - - s.tc = container - s.db = db - s.builder, err = schema.NewBuilder("mariadb") - s.Require().NoError(err) -} - -func (s *mariadbBuilderSuite) TearDownSuite() { - _ = s.db.Close() - if s.tc != nil { - _ = s.tc.Terminate(s.ctx) - } -} - -func (s *mariadbBuilderSuite) AfterTest(_, _ string) { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - c := schema.NewContext(s.ctx, tx) - tables, err := builder.GetTables(c) - s.Require().NoError(err) - for _, table := range tables { - err := builder.DropIfExists(c, table.Name) - if err != nil { - s.T().Logf("error dropping table %s: %v", table.Name, err) - } - } - err = tx.Commit() - s.Require().NoError(err, "expected no error when committing transaction after dropping tables") -} - -func (s *mariadbBuilderSuite) TestCreate() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when all parameters are valid, should create table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.UUID("uuid").Nullable() - table.Timestamps() - }) - s.Require().NoError(err) - }) -} - -func (s *mariadbBuilderSuite) TestGetColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when table exists, should return columns successfully", func() { - tableName := "all_types" - err = builder.Create(c, tableName, func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.UUID("guid") - table.Point("location") - }) - s.Require().NoError(err) - - columns, err := builder.GetColumns(c, tableName) - s.Require().NoError(err) - s.Len(columns, 4) - - columnMap := make(map[string]*schema.Column) - for _, col := range columns { - columnMap[col.Name] = col - } - - s.Contains(columnMap, "id") - s.Contains(columnMap, "name") - s.Contains(columnMap, "guid") - s.Contains(columnMap, "location") - - s.Equal("uuid", columnMap["guid"].TypeName) - }) -} diff --git a/schema/builders/mysql_builder_test.go b/schema/builders/mysql_builder_test.go deleted file mode 100644 index 3146f0f..0000000 --- a/schema/builders/mysql_builder_test.go +++ /dev/null @@ -1,639 +0,0 @@ -package builders_test - -import ( - "context" - "database/sql" - "testing" - - "github.com/akfaiz/migris/internal/testutil" - "github.com/akfaiz/migris/schema" - "github.com/stretchr/testify/suite" - "github.com/testcontainers/testcontainers-go" -) - -func TestMysqlBuilderSuite(t *testing.T) { - suite.Run(t, new(mysqlBuilderSuite)) -} - -type mysqlBuilderSuite struct { - suite.Suite - - ctx context.Context - db *sql.DB - builder schema.Builder - tc testcontainers.Container -} - -func (s *mysqlBuilderSuite) SetupSuite() { - s.ctx = context.Background() - - container, db, err := testutil.StartMySQLTestDB(s.ctx) - s.Require().NoError(err) - - s.tc = container - s.db = db - s.builder, err = schema.NewBuilder("mysql") - s.Require().NoError(err) -} - -func (s *mysqlBuilderSuite) TearDownSuite() { - _ = s.db.Close() - if s.tc != nil { - _ = s.tc.Terminate(s.ctx) - } -} - -func (s *mysqlBuilderSuite) AfterTest(_, _ string) { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - c := schema.NewContext(s.ctx, tx) - tables, err := builder.GetTables(c) - s.Require().NoError(err) - for _, table := range tables { - err := builder.DropIfExists(c, table.Name) - if err != nil { - s.T().Logf("error dropping table %s: %v", table.Name, err) - } - } - err = tx.Commit() - s.Require().NoError(err, "expected no error when committing transaction after dropping tables") -} - -func (s *mysqlBuilderSuite) TestCreate() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Create(nil, "test_table", func(table *schema.Blueprint) { - table.String("name") - }) - s.Require().Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.Create(c, "", func(table *schema.Blueprint) { - table.String("name") - }) - s.Require().Error(err) - }) - s.Run("when blueprint is nil, should return error", func() { - err := builder.Create(c, "test_table", nil) - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should create table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table with valid parameters") - }) - s.Run("when have composite primary key should create it successfully", func() { - err = builder.Create(c, "user_roles", func(table *schema.Blueprint) { - table.Integer("user_id") - table.Integer("role_id") - - table.Primary("user_id", "role_id") - }) - s.Require().NoError(err, "expected no error when creating table with composite primary key") - }) - s.Run("when have foreign key should create it successfully", func() { - err = builder.Create(c, "orders", func(table *schema.Blueprint) { - table.ID() - table.UnsignedBigInteger("user_id") - table.String("order_id", 255).Unique() - table.Decimal("amount", 10, 2) - table.Timestamp("created_at").UseCurrent() - - table.Foreign("user_id").References("id").On("users").OnDelete("CASCADE").OnUpdate("CASCADE") - }) - s.Require().NoError(err, "expected no error when creating table with foreign key") - }) - s.Run("when have custom index should create it successfully", func() { - err = builder.Create(c, "orders_2", func(table *schema.Blueprint) { - table.ID() - table.String("order_id", 255).Unique("uk_orders_2_order_id") - table.Decimal("amount", 10, 2) - table.Timestamp("created_at").UseCurrent() - - table.Index("created_at").Name("idx_orders_created_at").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when creating table with custom index") - }) - s.Run("when table already exists, should return error", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - }) - s.Require().Error(err, "expected error when creating table that already exists") - }) -} - -func (s *mysqlBuilderSuite) TestDrop() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Drop(nil, "test_table") - s.Require().Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.Drop(c, "") - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should drop table successfully", func() { - _ = builder.DropIfExists(c, "users_drop_test") - err = builder.Create(c, "users_drop_test", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before dropping it") - err = builder.Drop(c, "users_drop_test") - s.Require().NoError(err, "expected no error when dropping table with valid parameters") - }) - s.Run("when table does not exist, should return error", func() { - err = builder.Drop(c, "non_existent_table") - s.Require().Error(err, "expected error when dropping a table that does not exist") - }) -} - -func (s *mysqlBuilderSuite) TestDropIfExists() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.DropIfExists(nil, "test_table") - s.Require().Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.DropIfExists(c, "") - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should drop table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before dropping it") - err = builder.DropIfExists(c, "users") - s.Require().NoError(err, "expected no error when dropping table with valid parameters") - }) - s.Run("when table does not exist, should not return error", func() { - err = builder.DropIfExists(c, "non_existent_table") - s.Require().NoError(err, "expected no error when dropping a table that does not exist with DropIfExists") - }) -} - -func (s *mysqlBuilderSuite) TestRename() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Rename(nil, "old_table", "new_table") - s.Require().Error(err) - }) - s.Run("when old table name is empty, should return error", func() { - err := builder.Rename(c, "", "new_table") - s.Require().Error(err) - }) - s.Run("when new table name is empty, should return error", func() { - err := builder.Rename(c, "old_table", "") - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should rename table successfully", func() { - err = builder.Create(c, "old_table", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - }) - s.Require().NoError(err, "expected no error when creating old_table before renaming it") - err = builder.Rename(c, "old_table", "new_table") - s.Require().NoError(err, "expected no error when renaming table with valid parameters") - }) -} - -func (s *mysqlBuilderSuite) TestTable() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Table(nil, "test_table", func(table *schema.Blueprint) { - table.String("name") - }) - s.Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.Table(c, "", func(table *schema.Blueprint) { - table.String("name") - }) - s.Error(err) - }) - s.Run("when blueprint is nil, should return error", func() { - err := builder.Table(c, "test_table", nil) - s.Error(err) - }) - s.Run("when all parameters are valid, should modify table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique("uk_users_email") - table.String("password", 255).Nullable() - table.Text("bio").Nullable() - table.Timestamps() - - table.FullText("bio") - }) - s.Require().NoError(err, "expected no error when creating table before modifying it") - - s.Run("should add new columns and modify existing ones", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.String("address", 255).Nullable() - table.String("phone", 20).Nullable().Unique("uk_users_phone") - }) - s.Require().NoError(err, "expected no error when modifying table with valid parameters") - }) - s.Run("should modify existing column", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.String("email", 255).Nullable().Change() - }) - s.Require().NoError(err, "expected no error when modifying existing column") - }) - s.Run("should drop column and rename existing one", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropColumn("password") - table.RenameColumn("name", "full_name") - }) - s.Require().NoError(err, "expected no error when dropping column and renaming existing one") - }) - s.Run("should add index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.Index("phone").Name("idx_users_phone").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when adding index to table") - }) - s.Run("should rename index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.RenameIndex("idx_users_phone", "idx_users_contact") - }) - s.Require().NoError(err, "expected no error when renaming index in table") - }) - s.Run("should drop index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropIndex("idx_users_contact") - }) - s.Require().NoError(err, "expected no error when dropping index from table") - }) - s.Run("should drop unique constraint", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropUnique("uk_users_email") - }) - s.Require().NoError(err, "expected no error when dropping unique constraint from table") - }) - s.Run("should drop fulltext index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropFulltext([]string{"bio"}) - }) - s.Require().NoError(err, "expected no error when dropping fulltext index from table") - }) - s.Run("should add foreign key", func() { - err = builder.Create(c, "roles", func(table *schema.Blueprint) { - table.UnsignedInteger("id").Primary() - table.String("role_name", 255).Unique("uk_roles_role_name") - }) - s.Require().NoError(err, "expected no error when creating roles table before adding foreign key") - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.UnsignedInteger("role_id").Nullable() - table.Foreign("role_id").References("id").On("roles").OnDelete("SET NULL").OnUpdate("CASCADE") - }) - s.Require().NoError(err, "expected no error when adding foreign key to users table") - }) - s.Run("should drop foreign key", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropForeign([]string{"role_id"}) - }) - s.Require().NoError(err, "expected no error when dropping foreign key from users table") - }) - s.Run("should drop primary key", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.UnsignedBigInteger("id").Change() - table.DropPrimary("users_pkey") - }) - s.Require().NoError(err, "expected no error when dropping primary key from users table") - }) - }) -} - -func (s *mysqlBuilderSuite) TestGetColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - columns, err := builder.GetColumns(nil, "test_table") - s.Require().Error(err) - s.Nil(columns) - }) - s.Run("when table name is empty, should return error", func() { - columns, err := builder.GetColumns(c, "") - s.Require().Error(err) - s.Nil(columns) - }) - s.Run("when table does not exist, should return empty slice", func() { - columns, err := builder.GetColumns(c, "non_existent_table") - s.Require().NoError(err) - s.Empty(columns) - }) - s.Run("when table exists, should return columns successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before getting columns") - - columns, err := builder.GetColumns(c, "users") - s.Require().NoError(err, "expected no error when getting columns from existing table") - s.NotEmpty(columns) - s.Len(columns, 6, "expected 6 columns in the users table") - }) -} - -func (s *mysqlBuilderSuite) TestGetIndexes() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - _, err := builder.GetIndexes(nil, "users_indexes") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - _, err := builder.GetIndexes(c, "") - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - - table.Index("name").Name("idx_users_name") - }) - s.Require().NoError(err, "expected no error when creating table before getting indexes") - - indexes, err := builder.GetIndexes(c, "users") - s.Require().NoError(err, "expected no error when getting indexes with valid parameters") - s.Len(indexes, 3, "expected 3 index to be returned") - }) - s.Run("when table does not exist, should return empty indexes", func() { - indexes, err := builder.GetIndexes(c, "non_existent_table") - s.Require().NoError(err, "expected no error when getting indexes of non-existent table") - s.Empty(indexes, "expected empty indexes for non-existent table") - }) -} - -func (s *mysqlBuilderSuite) TestGetTables() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when tx is nil, should return error", func() { - tables, err := builder.GetTables(nil) - s.Require().Error(err, "expected error when transaction is nil") - s.Nil(tables) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before getting tables") - - tables, err := builder.GetTables(c) - s.Require().NoError(err, "expected no error when getting tables after creating one") - s.NotEmpty(tables, "expected non-empty tables slice after creating a table") - found := false - for _, table := range tables { - if table.Name == "users" { - found = true - break - } - } - s.True(found, "expected users to be in the list of tables") - }) -} - -func (s *mysqlBuilderSuite) TestHasColumn() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasColumn(nil, "users", "name") - s.Require().Error(err, "expected error when context is nil") - s.False(exists) - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasColumn(c, "", "name") - s.Require().Error(err, "expected error when table name is empty") - s.False(exists) - }) - s.Run("when column name is empty, should return error", func() { - exists, err := builder.HasColumn(c, "users", "") - s.Require().Error(err, "expected error when column name is empty") - s.False(exists) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking for column existence") - - exists, err := builder.HasColumn(c, "users", "name") - s.Require().NoError(err, "expected no error when checking for existing column") - s.True(exists, "expected 'name' column to exist in users table") - - exists, err = builder.HasColumn(c, "users", "non_existent_column") - s.Require().NoError(err, "expected no error when checking for non-existing column") - s.False(exists, "expected 'non_existent_column' to not exist in users table") - }) -} - -func (s *mysqlBuilderSuite) TestHasColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasColumns(nil, "users", []string{"name"}) - s.Require().Error(err, "expected error when context is nil") - s.False(exists) - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasColumns(c, "", []string{"name"}) - s.Require().Error(err, "expected error when table name is empty") - s.False(exists) - }) - s.Run("when column names are empty, should return error", func() { - exists, err := builder.HasColumns(c, "users", []string{}) - s.Require().Error(err, "expected error when column names are empty") - s.False(exists) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking for columns existence") - - exists, err := builder.HasColumns(c, "users", []string{"name", "email"}) - s.Require().NoError(err, "expected no error when checking for existing columns") - s.True(exists, "expected 'name' and 'email' columns to exist in users_has_columns table") - - exists, err = builder.HasColumns(c, "users", []string{"name", "non_existent_column"}) - s.Require().NoError(err, "expected no error when checking for mixed existing and non-existing columns") - s.False(exists, "expected 'non_existent_column' to not exist in users_has_columns table") - }) -} - -func (s *mysqlBuilderSuite) TestHasIndex() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasIndex(nil, "orders", []string{"idx_users_name"}) - s.Require().Error(err, "expected error when context is nil") - s.False(exists, "expected exists to be false when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasIndex(c, "", []string{"idx_users_name"}) - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "orders", func(table *schema.Blueprint) { - table.ID() - table.Integer("company_id") - table.Integer("user_id") - table.String("order_id", 255) - table.Decimal("amount", 10, 2) - table.Timestamps() - - table.Index("company_id", "user_id") - table.Unique("order_id").Name("uk_orders3_order_id").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when creating table with index") - - exists, err := builder.HasIndex(c, "orders", []string{"uk_orders3_order_id"}) - s.Require().NoError(err, "expected no error when checking if index exists with valid parameters") - s.True(exists, "expected exists to be true for existing index") - - exists, err = builder.HasIndex(c, "orders", []string{"company_id", "user_id"}) - s.Require().NoError(err, "expected no error when checking non-existent index") - s.True(exists, "expected exists to be true for existing composite index") - - exists, err = builder.HasIndex(c, "orders", []string{"non_existent_index"}) - s.Require().NoError(err, "expected no error when checking non-existent index") - s.False(exists, "expected exists to be false for non-existent index") - }) -} - -func (s *mysqlBuilderSuite) TestHasTable() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasTable(nil, "users") - s.Require().Error(err, "expected error when context is nil") - s.False(exists, "expected exists to be false when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasTable(c, "") - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking if it exists") - - var exists bool - exists, err = builder.HasTable(c, "users") - s.Require().NoError(err, "expected no error when checking if table exists with valid parameters") - s.True(exists, "expected exists to be true for existing table") - - exists, err = builder.HasTable(c, "non_existent_table") - s.Require().NoError(err, "expected no error when checking non-existent table") - s.False(exists, "expected exists to be false for non-existent table") - }) -} diff --git a/schema/builders/postgres_builder_test.go b/schema/builders/postgres_builder_test.go deleted file mode 100644 index c19bb52..0000000 --- a/schema/builders/postgres_builder_test.go +++ /dev/null @@ -1,634 +0,0 @@ -package builders_test - -import ( - "context" - "database/sql" - "testing" - - "github.com/akfaiz/migris/internal/testutil" - "github.com/akfaiz/migris/schema" - "github.com/stretchr/testify/suite" - "github.com/testcontainers/testcontainers-go" -) - -func TestPostgresBuilderSuite(t *testing.T) { - suite.Run(t, new(postgresBuilderSuite)) -} - -type postgresBuilderSuite struct { - suite.Suite - - ctx context.Context - db *sql.DB - builder schema.Builder - tc testcontainers.Container -} - -func (s *postgresBuilderSuite) SetupSuite() { - s.ctx = context.Background() - - container, db, err := testutil.StartPostgresTestDB(s.ctx) - s.Require().NoError(err) - - s.tc = container - s.db = db - s.builder, err = schema.NewBuilder("postgres") - s.Require().NoError(err) -} - -func (s *postgresBuilderSuite) TearDownSuite() { - _ = s.db.Close() - if s.tc != nil { - _ = s.tc.Terminate(s.ctx) - } -} - -func (s *postgresBuilderSuite) TestCreate() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err = builder.Create(nil, "test_table", func(_ *schema.Blueprint) {}) - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - err = builder.Create(c, "", func(_ *schema.Blueprint) {}) - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when blueprint is nil, should return error", func() { - err = builder.Create(c, "test_table", nil) - s.Require().Error(err, "expected error when blueprint is nil") - }) - s.Run("when all parameters are valid, should create table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name") - table.String("email").Unique() - table.String("password").Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table with valid parameters") - }) - s.Run("when use custom schema should create it successfully", func() { - _, err = tx.Exec("CREATE SCHEMA IF NOT EXISTS custom_public") - s.Require().NoError(err, "expected no error when creating custom schema") - err = builder.Create(c, "custom_public.users", func(table *schema.Blueprint) { - table.ID() - table.String("name") - table.String("email").Unique() - table.String("password").Nullable() - table.TimestampsTz() - }) - s.Require().NoError(err, "expected no error when creating table with custom schema") - }) - s.Run("when have composite primary key should create it successfully", func() { - err = builder.Create(c, "user_roles", func(table *schema.Blueprint) { - table.Integer("user_id") - table.Integer("role_id") - - table.Primary("user_id", "role_id") - }) - s.Require().NoError(err, "expected no error when creating table with composite primary key") - }) - s.Run("when have foreign key should create it successfully", func() { - err = builder.Create(c, "orders", func(table *schema.Blueprint) { - table.ID() - table.BigInteger("user_id") - table.String("order_id").Unique() - table.Decimal("amount", 10, 2) - table.Timestamp("created_at").UseCurrent() - - table.Foreign("user_id").References("id").On("users").OnDelete("CASCADE").OnUpdate("CASCADE") - }) - s.Require().NoError(err, "expected no error when creating table with foreign key") - }) - s.Run("when have custom index should create it successfully", func() { - err = builder.Create(c, "orders_2", func(table *schema.Blueprint) { - table.ID() - table.String("order_id").Unique("uk_orders_2_order_id") - table.Decimal("amount", 10, 2) - table.Timestamp("created_at").UseCurrent() - - table.Index("created_at").Name("idx_orders_created_at").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when creating table with custom index") - }) - s.Run("when table already exists, should return error", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name") - table.String("email").Unique() - }) - s.Require().Error(err, "expected error when creating table that already exists") - }) -} - -func (s *postgresBuilderSuite) TestDrop() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err = builder.Drop(nil, "test_table") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - err = builder.Drop(c, "") - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when all parameters are valid, should drop table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name") - table.String("email").Unique() - table.String("password").Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before dropping it") - err = builder.Drop(c, "users") - s.Require().NoError(err, "expected no error when dropping table with valid parameters") - }) - s.Run("when table does not exist, should return error", func() { - err = builder.Drop(c, "non_existent_table") - s.Require().Error(err, "expected error when dropping table that does not exist") - }) -} - -func (s *postgresBuilderSuite) TestDropIfExists() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err = builder.DropIfExists(nil, "test_table") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - err = builder.DropIfExists(c, "") - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when context is nil, should return error", func() { - err = builder.DropIfExists(nil, "test_table") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when all parameters are valid, should drop table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name") - table.String("email").Unique() - table.String("password").Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before dropping it") - err = builder.DropIfExists(c, "users") - s.Require().NoError(err, "expected no error when dropping table with valid parameters") - }) - s.Run("when table does not exist, should not return error", func() { - err = builder.DropIfExists(c, "non_existent_table") - s.Require().NoError(err, "expected no error when dropping non-existent table with IF EXISTS clause") - }) -} - -func (s *postgresBuilderSuite) TestRename() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err = builder.Rename(nil, "old_table", "new_table") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when old table name is empty, should return error", func() { - err = builder.Rename(c, "", "new_table") - s.Require().Error(err, "expected error when old table name is empty") - }) - s.Run("when new table name is empty, should return error", func() { - err = builder.Rename(c, "old_table", "") - s.Require().Error(err, "expected error when new table name is empty") - }) - s.Run("when all parameters are valid, should rename table successfully", func() { - err = builder.Create(c, "old_table", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - }) - s.Require().NoError(err, "expected no error when creating table before renaming it") - err = builder.Rename(c, "old_table", "new_table") - s.Require().NoError(err, "expected no error when renaming table with valid parameters") - }) - s.Run("when renaming non-existent table, should return error", func() { - err = builder.Rename(c, "non_existent_table", "new_table") - s.Require().Error(err, "expected error when renaming non-existent table") - s.ErrorContains(err, "does not exist", "expected error message to contain 'does not exist'") - }) -} - -func (s *postgresBuilderSuite) TestTable() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err = builder.Table(nil, "test_table", func(_ *schema.Blueprint) {}) - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - err = builder.Table(c, "", func(_ *schema.Blueprint) {}) - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when blueprint is nil, should return error", func() { - err = builder.Table(c, "test_table", nil) - s.Require().Error(err, "expected error when blueprint is nil") - }) - s.Run("when all parameters are valid, should modify table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique("uk_users_email") - table.String("password", 255).Nullable() - table.Text("bio").Nullable() - table.Timestamps() - - table.FullText("bio") - }) - s.Require().NoError(err, "expected no error when creating table before modifying it") - - s.Run("should add new columns and modify existing ones", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.String("address", 255).Nullable() - table.String("phone", 20).Nullable().Unique("uk_users_phone") - }) - s.Require().NoError(err, "expected no error when modifying table with valid parameters") - }) - s.Run("should modify existing column", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.String("email", 255).Nullable().Change() - }) - s.Require().NoError(err, "expected no error when modifying existing column") - }) - s.Run("should drop column and rename existing one", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropColumn("password") - table.RenameColumn("name", "full_name") - }) - s.Require().NoError(err, "expected no error when dropping column and renaming existing one") - }) - s.Run("should add index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.Index("phone").Name("idx_users_phone").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when adding index to table") - }) - s.Run("should rename index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.RenameIndex("idx_users_phone", "idx_users_contact") - }) - s.Require().NoError(err, "expected no error when renaming index in table") - }) - s.Run("should drop index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropIndex("idx_users_contact") - }) - s.Require().NoError(err, "expected no error when dropping index from table") - }) - s.Run("should drop unique constraint", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropUnique("uk_users_email") - }) - s.Require().NoError(err, "expected no error when dropping unique constraint from table") - }) - s.Run("should drop fulltext index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropFulltext([]string{"bio"}) - }) - s.Require().NoError(err, "expected no error when dropping fulltext index from table") - }) - s.Run("should add foreign key", func() { - err = builder.Create(c, "roles", func(table *schema.Blueprint) { - table.ID() - table.String("role_name", 255).Unique("uk_roles_role_name") - }) - s.Require().NoError(err, "expected no error when creating roles table before adding foreign key") - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.Integer("role_id").Nullable() - table.Foreign("role_id").References("id").On("roles").OnDelete("SET NULL").OnUpdate("CASCADE") - }) - s.Require().NoError(err, "expected no error when adding foreign key to users table") - }) - s.Run("should drop foreign key", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropForeign([]string{"role_id"}) - }) - s.Require().NoError(err, "expected no error when dropping foreign key from users table") - }) - s.Run("should drop primary key", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropPrimary([]string{"id"}) - }) - s.Require().NoError(err, "expected no error when dropping primary key from users table") - }) - }) -} - -func (s *postgresBuilderSuite) TestGetColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - _, err = builder.GetColumns(nil, "users") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - _, err = builder.GetColumns(c, "") - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before getting columns") - - columns, err := builder.GetColumns(c, "users") - s.Require().NoError(err, "expected no error when getting columns with valid parameters") - s.Len(columns, 6, "expected 6 columns to be returned") - }) - s.Run("when table does not exist, should return empty columns", func() { - columns, err := builder.GetColumns(c, "non_existent_table") - s.Require().NoError(err, "expected no error when getting columns of non-existent table") - s.Empty(columns, "expected empty columns for non-existent table") - }) -} - -func (s *postgresBuilderSuite) TestGetIndexes() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - _, err := builder.GetIndexes(nil, "users") - s.Require().Error(err, "expected error when contexts is nil") - }) - s.Run("when table name is empty, should return error", func() { - _, err := builder.GetIndexes(c, "") - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - - table.Index("name").Name("idx_users_name") - }) - s.Require().NoError(err, "expected no error when creating table before getting indexes") - - indexes, err := builder.GetIndexes(c, "users") - s.Require().NoError(err, "expected no error when getting indexes with valid parameters") - s.Len(indexes, 3, "expected 3 index to be returned") - }) - s.Run("when table does not exist, should return empty indexes", func() { - indexes, err := builder.GetIndexes(c, "non_existent_table") - s.Require().NoError(err, "expected no error when getting indexes of non-existent table") - s.Empty(indexes, "expected empty indexes for non-existent table") - }) -} - -func (s *postgresBuilderSuite) TestGetTables() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - _, err := builder.GetTables(nil) - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before getting tables") - - tables, err := builder.GetTables(c) - s.Require().NoError(err, "expected no error when getting tables with valid parameters") - s.Len(tables, 1, "expected 1 table to be returned") - userTable := tables[0] - s.Equal("users", userTable.Name, "expected table name to be 'users'") - s.Equal("public", userTable.Schema, "expected table schema to be 'public'") - s.False(userTable.Comment.Valid, "expected table comment to be invalid (nil)") - }) -} - -func (s *postgresBuilderSuite) TestHasColumn() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when tx is nil, should return error", func() { - exists, err := builder.HasColumn(nil, "users", "name") - s.Require().Error(err, "expected error when transaction is nil") - s.False(exists, "expected exists to be false when transaction is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasColumn(c, "", "name") - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking column existence") - - exists, err := builder.HasColumn(c, "users", "name") - s.Require().NoError(err, "expected no error when checking if column exists with valid parameters") - s.True(exists, "expected exists to be true for existing column") - - exists, err = builder.HasColumn(c, "users", "non_existent_column") - s.Require().NoError(err, "expected no error when checking non-existent column") - s.False(exists, "expected exists to be false for non-existent column") - }) -} - -func (s *postgresBuilderSuite) TestHasColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when tx is nil, should return error", func() { - exists, err := builder.HasColumns(nil, "users", []string{"name"}) - s.Require().Error(err, "expected error when transaction is nil") - s.False(exists, "expected exists to be false when transaction is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasColumns(c, "", []string{"name"}) - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking column existence") - - exists, err := builder.HasColumns(c, "users", []string{"name", "email"}) - s.Require().NoError(err, "expected no error when checking if columns exist with valid parameters") - s.True(exists, "expected exists to be true for existing columns") - - exists, err = builder.HasColumns(c, "users", []string{"name", "non_existent_column"}) - s.Require().NoError(err, "expected no error when checking mixed existing and non-existent columns") - s.False(exists, "expected exists to be false for mixed existing and non-existent columns") - }) -} - -func (s *postgresBuilderSuite) TestHasIndex() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasIndex(nil, "users", []string{"idx_users_name"}) - s.Require().Error(err, "expected error when context is nil") - s.False(exists, "expected exists to be false when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasIndex(c, "", []string{"idx_users_name"}) - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "orders", func(table *schema.Blueprint) { - table.ID() - table.Integer("company_id") - table.Integer("user_id") - table.String("order_id", 255) - table.Decimal("amount", 10, 2) - table.Timestamp("created_at").UseCurrent() - - table.Index("company_id", "user_id") - table.Unique("order_id").Name("uk_orders_order_id") - }) - s.Require().NoError(err, "expected no error when creating table with index") - - exists, err := builder.HasIndex(c, "orders", []string{"uk_orders_order_id"}) - s.Require().NoError(err, "expected no error when checking if index exists with valid parameters") - s.True(exists, "expected exists to be true for existing index") - - exists, err = builder.HasIndex(c, "orders", []string{"company_id", "user_id"}) - s.Require().NoError(err, "expected no error when checking non-existent index") - s.True(exists, "expected exists to be true for existing composite index") - - exists, err = builder.HasIndex(c, "orders", []string{"non_existent_index"}) - s.Require().NoError(err, "expected no error when checking non-existent index") - s.False(exists, "expected exists to be false for non-existent index") - }) -} - -func (s *postgresBuilderSuite) TestHasTable() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasTable(nil, "users") - s.Require().Error(err, "expected error when context is nil") - s.False(exists, "expected exists to be false when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasTable(c, "") - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking existence") - - exists, err := builder.HasTable(c, "users") - s.Require().NoError(err, "expected no error when checking if table exists with valid parameters") - s.True(exists, "expected exists to be true for existing table") - - exists, err = builder.HasTable(c, "non_existent_table") - s.Require().NoError(err, "expected no error when checking non-existent table") - s.False(exists, "expected exists to be false for non-existent table") - }) - s.Run("when checking table with custom schema, should return true if exists", func() { - _, err = tx.Exec("CREATE SCHEMA IF NOT EXISTS custom_publics") - s.Require().NoError(err, "expected no error when creating custom schema") - - err = builder.Create(c, "custom_publics.users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table with custom schema") - - exists, err := builder.HasTable(c, "custom_publics.users") - s.Require().NoError(err, "expected no error when checking if table with custom schema exists") - s.True(exists, "expected exists to be true for existing table with custom schema") - exists, err = builder.HasTable(c, "custom_publics.non_existent_table") - s.Require().NoError(err, "expected no error when checking non-existent table with custom schema") - s.False(exists, "expected exists to be false for non-existent table with custom schema") - }) -} diff --git a/schema/builders/sqlite_builder_test.go b/schema/builders/sqlite_builder_test.go deleted file mode 100644 index 2919cd7..0000000 --- a/schema/builders/sqlite_builder_test.go +++ /dev/null @@ -1,574 +0,0 @@ -package builders_test - -import ( - "context" - "database/sql" - "os" - "strings" - "testing" - - "github.com/akfaiz/migris/schema" - _ "github.com/mattn/go-sqlite3" - "github.com/stretchr/testify/suite" -) - -func TestSqliteBuilderSuite(t *testing.T) { - suite.Run(t, new(sqliteBuilderSuite)) -} - -type sqliteBuilderSuite struct { - suite.Suite - - ctx context.Context - db *sql.DB - builder schema.Builder - dbFile string -} - -func (s *sqliteBuilderSuite) SetupSuite() { - s.ctx = context.Background() - - // Use a temporary file for SQLite database - s.dbFile = "test_sqlite_builder.db" - - db, err := sql.Open("sqlite3", s.dbFile) - s.Require().NoError(err) - - err = db.Ping() - s.Require().NoError(err) - - s.db = db - s.builder, err = schema.NewBuilder("sqlite3") - s.Require().NoError(err) -} - -func (s *sqliteBuilderSuite) TearDownSuite() { - _ = s.db.Close() - // Clean up the test database file - _ = os.Remove(s.dbFile) -} - -func (s *sqliteBuilderSuite) AfterTest(_, _ string) { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - c := schema.NewContext(s.ctx, tx) - tables, err := builder.GetTables(c) - s.Require().NoError(err) - for _, table := range tables { - err := builder.DropIfExists(c, table.Name) - if err != nil { - s.T().Logf("error dropping table %s: %v", table.Name, err) - } - } - err = tx.Commit() - s.Require().NoError(err, "expected no error when committing transaction after dropping tables") -} - -func (s *sqliteBuilderSuite) TestCreate() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Create(nil, "test_table", func(table *schema.Blueprint) { - table.String("name") - }) - s.Require().Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.Create(c, "", func(table *schema.Blueprint) { - table.String("name") - }) - s.Require().Error(err) - }) - s.Run("when blueprint is nil, should return error", func() { - err := builder.Create(c, "test_table", nil) - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should create table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table with valid parameters") - }) - s.Run("when have custom index should create it successfully", func() { - err = builder.Create(c, "orders_2", func(table *schema.Blueprint) { - table.ID() - table.String("order_id", 255).Unique("uk_orders_2_order_id") - table.Decimal("amount", 10, 2) - table.Timestamp("created_at").UseCurrent() - - table.Index("created_at").Name("idx_orders_created_at").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when creating table with custom index") - }) - s.Run("when table already exists, should return error", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - }) - s.Require().Error(err, "expected error when creating table that already exists") - }) -} - -func (s *sqliteBuilderSuite) TestDrop() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Drop(nil, "test_table") - s.Require().Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.Drop(c, "") - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should drop table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before dropping it") - err = builder.Drop(c, "users") - s.Require().NoError(err, "expected no error when dropping table with valid parameters") - }) - s.Run("when table does not exist, should return error", func() { - err = builder.Drop(c, "non_existent_table") - s.Require().Error(err, "expected error when dropping table that does not exist") - }) -} - -func (s *sqliteBuilderSuite) TestDropIfExists() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.DropIfExists(nil, "test_table") - s.Require().Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.DropIfExists(c, "") - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should drop table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before dropping it") - err = builder.DropIfExists(c, "users") - s.Require().NoError(err, "expected no error when dropping table with valid parameters") - }) - s.Run("when table does not exist, should not return error", func() { - err = builder.DropIfExists(c, "non_existent_table") - s.Require().NoError(err, "expected no error when dropping a table that does not exist with DropIfExists") - }) -} - -func (s *sqliteBuilderSuite) TestRename() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Rename(nil, "old_table", "new_table") - s.Require().Error(err) - }) - s.Run("when old table name is empty, should return error", func() { - err := builder.Rename(c, "", "new_table") - s.Require().Error(err) - }) - s.Run("when new table name is empty, should return error", func() { - err := builder.Rename(c, "old_table", "") - s.Require().Error(err) - }) - s.Run("when all parameters are valid, should rename table successfully", func() { - err = builder.Create(c, "old_table", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - }) - s.Require().NoError(err, "expected no error when creating old_table before renaming it") - err = builder.Rename(c, "old_table", "new_table") - s.Require().NoError(err, "expected no error when renaming table with valid parameters") - }) -} - -func (s *sqliteBuilderSuite) TestTable() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - err := builder.Table(nil, "test_table", func(table *schema.Blueprint) { - table.String("name") - }) - s.Error(err) - }) - s.Run("when table name is empty, should return error", func() { - err := builder.Table(c, "", func(table *schema.Blueprint) { - table.String("name") - }) - s.Error(err) - }) - s.Run("when blueprint is nil, should return error", func() { - err := builder.Table(c, "test_table", nil) - s.Error(err) - }) - s.Run("when all parameters are valid, should modify table successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique("uk_users_email") - table.String("password", 255).Nullable() - table.Text("bio").Nullable() - table.Timestamps() - - // Note: SQLite FullText requires FTS virtual tables, skip for regular table test - }) - s.Require().NoError(err, "expected no error when creating table before modifying it") - - s.Run("should add new columns and modify existing ones", func() { - // SQLite supports adding columns but has limitations - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.String("address", 255).Nullable() - // Note: Multiple column additions may require individual statements - }) - // This test may fail due to SQLite syntax limitations - skip if needed - if err != nil && strings.Contains(err.Error(), "syntax error") { - s.T().Skip("SQLite has limitations with multiple column additions in single statement") - } - s.Require().NoError(err, "expected no error when adding column with valid parameters") - }) - s.Run("should add index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.Index("bio").Name("idx_users_bio") - }) - s.Require().NoError(err, "expected no error when adding index to table") - }) - s.Run("should drop index", func() { - err = builder.Table(c, "users", func(table *schema.Blueprint) { - table.DropIndex("idx_users_bio") - }) - s.Require().NoError(err, "expected no error when dropping index from table") - }) - }) -} - -func (s *sqliteBuilderSuite) TestGetColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - columns, err := builder.GetColumns(nil, "test_table") - s.Require().Error(err) - s.Nil(columns) - }) - s.Run("when table name is empty, should return error", func() { - columns, err := builder.GetColumns(c, "") - s.Require().Error(err) - s.Nil(columns) - }) - s.Run("when table does not exist, should return empty slice", func() { - columns, err := builder.GetColumns(c, "non_existent_table") - s.Require().NoError(err) - s.Empty(columns) - }) - s.Run("when table exists, should return columns successfully", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before getting columns") - - columns, err := builder.GetColumns(c, "users") - s.Require().NoError(err, "expected no error when getting columns from existing table") - s.NotEmpty(columns) - s.Len(columns, 6, "expected 6 columns in the users table") - }) -} - -func (s *sqliteBuilderSuite) TestGetIndexes() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - _, err := builder.GetIndexes(nil, "users_indexes") - s.Require().Error(err, "expected error when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - _, err := builder.GetIndexes(c, "") - s.Require().Error(err, "expected error when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - - table.Index("name").Name("idx_users_name") - }) - s.Require().NoError(err, "expected no error when creating table before getting indexes") - - indexes, err := builder.GetIndexes(c, "users") - s.Require().NoError(err, "expected no error when getting indexes with valid parameters") - s.Len( - indexes, - 3, - "expected 3 indexes to be returned", - ) // SQLite: one for primary, one for email unique and one for name index - }) - s.Run("when table does not exist, should return empty indexes", func() { - indexes, err := builder.GetIndexes(c, "non_existent_table") - s.Require().NoError(err, "expected no error when getting indexes of non-existent table") - s.Empty(indexes, "expected empty indexes for non-existent table") - }) -} - -func (s *sqliteBuilderSuite) TestGetTables() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when tx is nil, should return error", func() { - tables, err := builder.GetTables(nil) - s.Require().Error(err, "expected error when transaction is nil") - s.Nil(tables) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before getting tables") - - tables, err := builder.GetTables(c) - s.Require().NoError(err, "expected no error when getting tables after creating one") - s.NotEmpty(tables, "expected non-empty tables slice after creating a table") - found := false - for _, table := range tables { - if table.Name == "users" { - found = true - break - } - } - s.True(found, "expected users to be in the list of tables") - }) -} - -func (s *sqliteBuilderSuite) TestHasColumn() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasColumn(nil, "users", "name") - s.Require().Error(err, "expected error when context is nil") - s.False(exists) - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasColumn(c, "", "name") - s.Require().Error(err, "expected error when table name is empty") - s.False(exists) - }) - s.Run("when column name is empty, should return error", func() { - exists, err := builder.HasColumn(c, "users", "") - s.Require().Error(err, "expected error when column name is empty") - s.False(exists) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking for column existence") - - exists, err := builder.HasColumn(c, "users", "name") - s.Require().NoError(err, "expected no error when checking for existing column") - s.True(exists, "expected 'name' column to exist in users table") - - exists, err = builder.HasColumn(c, "users", "non_existent_column") - s.Require().NoError(err, "expected no error when checking for non-existing column") - s.False(exists, "expected 'non_existent_column' to not exist in users table") - }) -} - -func (s *sqliteBuilderSuite) TestHasColumns() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasColumns(nil, "users", []string{"name"}) - s.Require().Error(err, "expected error when context is nil") - s.False(exists) - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasColumns(c, "", []string{"name"}) - s.Require().Error(err, "expected error when table name is empty") - s.False(exists) - }) - s.Run("when column names are empty, should return error", func() { - exists, err := builder.HasColumns(c, "users", []string{}) - s.Require().Error(err, "expected error when column names are empty") - s.False(exists) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking for columns existence") - - exists, err := builder.HasColumns(c, "users", []string{"name", "email"}) - s.Require().NoError(err, "expected no error when checking for existing columns") - s.True(exists, "expected 'name' and 'email' columns to exist in users_has_columns table") - - exists, err = builder.HasColumns(c, "users", []string{"name", "non_existent_column"}) - s.Require().NoError(err, "expected no error when checking for mixed existing and non-existing columns") - s.False(exists, "expected 'non_existent_column' to not exist in users_has_columns table") - }) -} - -func (s *sqliteBuilderSuite) TestHasIndex() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasIndex(nil, "orders", []string{"idx_users_name"}) - s.Require().Error(err, "expected error when context is nil") - s.False(exists, "expected exists to be false when context is nil") - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasIndex(c, "", []string{"idx_users_name"}) - s.Require().Error(err, "expected error when table name is empty") - s.False(exists, "expected exists to be false when table name is empty") - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "orders", func(table *schema.Blueprint) { - table.ID() - table.Integer("company_id") - table.Integer("user_id") - table.String("order_id", 255) - table.Decimal("amount", 10, 2) - table.Timestamps() - - table.Index("company_id", "user_id") - table.Unique("order_id").Name("uk_orders3_order_id").Algorithm("BTREE") - }) - s.Require().NoError(err, "expected no error when creating table with index") - - exists, err := builder.HasIndex(c, "orders", []string{"uk_orders3_order_id"}) - s.Require().NoError(err, "expected no error when checking if index exists with valid parameters") - s.True(exists, "expected exists to be true for existing index") - - exists, err = builder.HasIndex(c, "orders", []string{"company_id", "user_id"}) - s.Require().NoError(err, "expected no error when checking non-existent index") - s.True(exists, "expected exists to be true for existing composite index") - - exists, err = builder.HasIndex(c, "orders", []string{"non_existent_index"}) - s.Require().NoError(err, "expected no error when checking non-existent index") - s.False(exists, "expected exists to be false for non-existent index") - }) -} - -func (s *sqliteBuilderSuite) TestHasTable() { - builder := s.builder - tx, err := s.db.BeginTx(s.ctx, nil) - s.Require().NoError(err) - defer tx.Rollback() - - c := schema.NewContext(s.ctx, tx) - - s.Run("when context is nil, should return error", func() { - exists, err := builder.HasTable(nil, "users") - s.Require().Error(err, "expected error when context is nil") - s.False(exists) - }) - s.Run("when table name is empty, should return error", func() { - exists, err := builder.HasTable(c, "") - s.Require().Error(err, "expected error when table name is empty") - s.False(exists) - }) - s.Run("when all parameters are valid", func() { - err = builder.Create(c, "users", func(table *schema.Blueprint) { - table.ID() - table.String("name", 255) - table.String("email", 255).Unique() - table.String("password", 255).Nullable() - table.Timestamps() - }) - s.Require().NoError(err, "expected no error when creating table before checking for table existence") - - exists, err := builder.HasTable(c, "users") - s.Require().NoError(err, "expected no error when checking for existing table") - s.True(exists, "expected users table to exist") - - exists, err = builder.HasTable(c, "non_existent_table") - s.Require().NoError(err, "expected no error when checking for non-existing table") - s.False(exists, "expected non_existent_table to not exist") - }) -} From 2d6ea0b98f9ae02180d99314e50c57537479594e Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 22:58:27 +0700 Subject: [PATCH 3/7] test(integration): add database builder coverage --- go.mod | 96 ++--- go.sum | 299 ++++++++------- integration/helpers_test.go | 19 + integration/mariadb_test.go | 303 ++++++++++++++++ integration/mysql_test.go | 561 ++++++++++++++++++++++++++++ integration/postgres_test.go | 686 +++++++++++++++++++++++++++++++++++ integration/sqlite_test.go | 335 +++++++++++++++++ integration/suite_test.go | 13 + 8 files changed, 2118 insertions(+), 194 deletions(-) create mode 100644 integration/helpers_test.go create mode 100644 integration/mariadb_test.go create mode 100644 integration/mysql_test.go create mode 100644 integration/postgres_test.go create mode 100644 integration/sqlite_test.go create mode 100644 integration/suite_test.go diff --git a/go.mod b/go.mod index 278139f..4df820e 100644 --- a/go.mod +++ b/go.mod @@ -1,82 +1,90 @@ module github.com/akfaiz/migris -go 1.23.0 +go 1.25.0 require ( github.com/fatih/color v1.18.0 github.com/go-sql-driver/mysql v1.9.3 - github.com/jackc/pgx/v5 v5.7.5 + github.com/jackc/pgx/v5 v5.8.0 github.com/mattn/go-sqlite3 v1.14.33 - github.com/pressly/goose/v3 v3.26.0 + github.com/onsi/ginkgo/v2 v2.29.0 + github.com/onsi/gomega v1.41.0 + github.com/pressly/goose/v3 v3.27.0 github.com/stretchr/testify v1.11.1 - github.com/testcontainers/testcontainers-go v0.36.0 - github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 - github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 - github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 - golang.org/x/term v0.33.0 + github.com/testcontainers/testcontainers-go v0.42.0 + github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 + github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 + github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 + golang.org/x/term v0.43.0 ) require ( - dario.cat/mergo v1.0.1 // indirect - filippo.io/edwards25519 v1.1.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + dario.cat/mergo v1.0.2 // indirect + filippo.io/edwards25519 v1.2.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v28.0.1+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-connections v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/ebitengine/purego v0.8.2 // indirect + github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/gogo/protobuf v1.3.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.9 // indirect + github.com/klauspost/compress v1.18.6 // indirect + github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/moby/api v1.54.2 // indirect + github.com/moby/moby/client v0.4.1 // indirect + github.com/moby/patternmatcher v0.6.1 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect - github.com/moby/term v0.5.0 // indirect - github.com/morikuni/aec v1.0.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect - github.com/shirou/gopsutil/v4 v4.25.1 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect + github.com/shirou/gopsutil/v4 v4.26.4 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect + github.com/tklauser/go-sysconf v0.4.0 // indirect + github.com/tklauser/numcpus v0.12.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/metric v1.37.0 // indirect - go.opentelemetry.io/otel/sdk v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect - google.golang.org/grpc v1.75.0 // indirect - google.golang.org/protobuf v1.36.7 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/mod v0.35.0 // indirect + golang.org/x/net v0.54.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect + golang.org/x/tools v0.44.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b082292..3f54bca 100644 --- a/go.sum +++ b/go.sum @@ -1,82 +1,98 @@ -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= +github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= +github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= +github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= +github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= +github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= +github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -84,161 +100,144 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= -github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= +github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI= +github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= +github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/onsi/ginkgo/v2 v2.29.0 h1:rfh+ZFjgJhYWRoIqVf3Uwx/W20yLrcrE2h2GmYVRaag= +github.com/onsi/ginkgo/v2 v2.29.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= +github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= -github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= +github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= -github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= +github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 h1:JrBOApOZ25GOkA+wX4YW4+MxCZ4VB62TC41CFyOi+LI= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0/go.mod h1:6A8KkwZ+iOinzhyzvg3C082Xk0ihFyLDQads38fM07M= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 h1:WUYu/gB23et+T9XCqfmYJB7eWW8/1qqPCxNIaRbS0yk= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0/go.mod h1:ED7dKWk3JE/dMRJ3t45TNGf3h9/htQG5au5gd1DuQcw= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 h1:xTGNNsOD9IIssH0dnAGNUH+SD9GYWyaP2t5xD2lg0as= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0/go.mod h1:WKS3MGq1lzbVibIRnL08TOaf5bKWPxJe5frzyQfV4oY= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= -google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= -google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= -modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= +modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= -modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/integration/helpers_test.go b/integration/helpers_test.go new file mode 100644 index 0000000..402279f --- /dev/null +++ b/integration/helpers_test.go @@ -0,0 +1,19 @@ +package integration_test + +import "github.com/akfaiz/migris/schema" + +func colNames(cols []*schema.Column) []string { + out := make([]string, len(cols)) + for i, c := range cols { + out[i] = c.Name + } + return out +} + +func indexNames(idxs []*schema.Index) []string { + out := make([]string, len(idxs)) + for i, idx := range idxs { + out[i] = idx.Name + } + return out +} diff --git a/integration/mariadb_test.go b/integration/mariadb_test.go new file mode 100644 index 0000000..4417ce4 --- /dev/null +++ b/integration/mariadb_test.go @@ -0,0 +1,303 @@ +package integration_test + +import ( + "context" + "database/sql" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // Ginkgo specs use dot imports by convention. + . "github.com/onsi/gomega" //nolint:revive // Ginkgo specs use dot imports by convention. + "github.com/testcontainers/testcontainers-go" + + "github.com/akfaiz/migris/internal/testutil" + "github.com/akfaiz/migris/schema" +) + +var _ = Describe("MariaDB", Ordered, func() { + var ( + mbCtx context.Context + db *sql.DB + tc testcontainers.Container + builder schema.Builder + ) + + BeforeAll(func() { + var err error + mbCtx = context.Background() + tc, db, err = testutil.StartMariaDBTestDB(mbCtx) + Expect(err).NotTo(HaveOccurred()) + builder, err = schema.NewBuilder("mariadb") + Expect(err).NotTo(HaveOccurred()) + }) + + AfterAll(func() { + _ = db.Close() + if tc != nil { + _ = tc.Terminate(mbCtx) + } + }) + + dropAll := func() { + tx, err := db.BeginTx(mbCtx, nil) + if err != nil { + return + } + c := schema.NewContext(mbCtx, tx) + tables, err := builder.GetTables(c) + if err != nil { + _ = tx.Rollback() + return + } + for _, t := range tables { + _ = builder.DropIfExists(c, t.Name) + } + _ = tx.Commit() + } + + newCtx := func() (schema.Context, *sql.Tx) { + tx, err := db.BeginTx(mbCtx, nil) + Expect(err).NotTo(HaveOccurred()) + return schema.NewContext(mbCtx, tx), tx + } + + AfterEach(func() { + dropAll() + }) + + Describe("Create", func() { + It("creates table with common column types", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255).Unique() + t.String("password", 255).Nullable() + t.Timestamps() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with InnoDB engine", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "products", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.InnoDB() + })).To(Succeed()) + }) + + It("creates table with SoftDeletes", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with NumericMorphs", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "comments", func(t *schema.Blueprint) { + t.ID() + t.NumericMorphs("commentable") + t.Text("body") + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "comments", []string{"commentable_type", "commentable_id"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("creates table with ForeignID.Constrained", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + })).To(Succeed()) + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.ForeignID("user_id").Constrained() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "user_id") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with RawColumn", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "events", func(t *schema.Blueprint) { + t.ID() + t.RawColumn("payload", "MEDIUMBLOB") + t.String("source", 100) + })).To(Succeed()) + + has, err := builder.HasColumn(c, "events", "payload") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with AutoIncrementStartingValues", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "invoices", func(t *schema.Blueprint) { + t.ID() + t.String("ref", 50) + t.AutoIncrementStartingValues(1000) + })).To(Succeed()) + }) + + It("returns error for duplicate table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { t.ID() })).To(HaveOccurred()) + }) + }) + + Describe("Table (Alter)", func() { + It("adds columns", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("phone", 20).Nullable() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "phone") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds column with After positioning", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("name", 255).After("id") + })).To(Succeed()) + }) + + It("drops SoftDeletes column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "articles", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + Expect(builder.Table(c, "articles", func(t *schema.Blueprint) { + t.DropSoftDeletes() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "articles", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("drops column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("bio", 1000).Nullable() + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropColumn("bio") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "bio") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + }) + + Describe("Drop / DropIfExists / Rename", func() { + It("drops table and DropIfExists are idempotent", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Drop(c, "tmp")).To(Succeed()) + Expect(builder.DropIfExists(c, "tmp")).To(Succeed()) + }) + + It("renames table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "src", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Rename(c, "src", "dst")).To(Succeed()) + has, err := builder.HasTable(c, "dst") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + }) + + Describe("Introspection", func() { + It("GetColumns, GetTables, HasIndex work correctly", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255).Unique("uk_users_email") + t.String("name", 255) + t.Timestamps() + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(cols).To(HaveLen(5)) + + has, err := builder.HasIndex(c, "users", []string{"email"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + + tables, err := builder.GetTables(c) + Expect(err).NotTo(HaveOccurred()) + names := make([]string, len(tables)) + for i, tb := range tables { + names[i] = tb.Name + } + Expect(names).To(ContainElement("users")) + }) + }) +}) diff --git a/integration/mysql_test.go b/integration/mysql_test.go new file mode 100644 index 0000000..2ebc4c4 --- /dev/null +++ b/integration/mysql_test.go @@ -0,0 +1,561 @@ +package integration_test + +import ( + "context" + "database/sql" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // Ginkgo specs use dot imports by convention. + . "github.com/onsi/gomega" //nolint:revive // Ginkgo specs use dot imports by convention. + "github.com/testcontainers/testcontainers-go" + + "github.com/akfaiz/migris/internal/testutil" + "github.com/akfaiz/migris/schema" +) + +var _ = Describe("MySQL", Ordered, func() { + var ( + mCtx context.Context + db *sql.DB + tc testcontainers.Container + builder schema.Builder + ) + + BeforeAll(func() { + var err error + mCtx = context.Background() + tc, db, err = testutil.StartMySQLTestDB(mCtx) + Expect(err).NotTo(HaveOccurred()) + builder, err = schema.NewBuilder("mysql") + Expect(err).NotTo(HaveOccurred()) + }) + + AfterAll(func() { + _ = db.Close() + if tc != nil { + _ = tc.Terminate(mCtx) + } + }) + + // dropAll removes all tables from the test database. MySQL DDL auto-commits, + // so transactions cannot roll back table creation — explicit cleanup is required. + dropAll := func() { + tx, err := db.BeginTx(mCtx, nil) + if err != nil { + return + } + c := schema.NewContext(mCtx, tx) + tables, err := builder.GetTables(c) + if err != nil { + _ = tx.Rollback() + return + } + for _, t := range tables { + _ = builder.DropIfExists(c, t.Name) + } + _ = tx.Commit() + } + + newCtx := func() (schema.Context, *sql.Tx) { + tx, err := db.BeginTx(mCtx, nil) + Expect(err).NotTo(HaveOccurred()) + return schema.NewContext(mCtx, tx), tx + } + + AfterEach(func() { + dropAll() + }) + + Describe("Create", func() { + It("returns error for nil context", func() { + Expect(builder.Create(nil, "t", func(_ *schema.Blueprint) {})).To(HaveOccurred()) + }) + + It("returns error for empty table name", func() { + c, tx := newCtx() + defer tx.Rollback() + Expect(builder.Create(c, "", func(_ *schema.Blueprint) {})).To(HaveOccurred()) + }) + + It("returns error for nil blueprint", func() { + c, tx := newCtx() + defer tx.Rollback() + Expect(builder.Create(c, "t", nil)).To(HaveOccurred()) + }) + + It("creates table with common column types", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255).Unique() + t.String("password", 255).Nullable() + t.Boolean("active").Default(true) + t.Timestamps() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with InnoDB engine", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "products", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.InnoDB() + })).To(Succeed()) + + has, err := builder.HasTable(c, "products") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with charset and collation", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.Charset("utf8mb4") + t.Collation("utf8mb4_unicode_ci") + })).To(Succeed()) + }) + + It("creates table with composite primary key", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "user_roles", func(t *schema.Blueprint) { + t.Integer("user_id") + t.Integer("role_id") + t.Primary("user_id", "role_id") + })).To(Succeed()) + }) + + It("creates table with foreign key and CASCADE actions", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + })).To(Succeed()) + + Expect(builder.Create(c, "orders", func(t *schema.Blueprint) { + t.ID() + t.UnsignedBigInteger("user_id") + t.Decimal("amount", 10, 2) + t.Foreign("user_id").References("id").On("users").OnDelete("CASCADE").OnUpdate("CASCADE") + })).To(Succeed()) + }) + + It("creates table with SoftDeletes", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with NumericMorphs", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "comments", func(t *schema.Blueprint) { + t.ID() + t.NumericMorphs("commentable") + t.Text("body") + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "comments", []string{"commentable_type", "commentable_id"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("creates table with UUIDMorphs", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "taggables", func(t *schema.Blueprint) { + t.ID() + t.UUIDMorphs("taggable") + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "taggables") + Expect(err).NotTo(HaveOccurred()) + Expect(colNames(cols)).To(ContainElements("taggable_type", "taggable_id")) + }) + + It("creates table with ForeignID.Constrained", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + })).To(Succeed()) + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.ForeignID("user_id").Constrained() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "user_id") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with RawColumn definition", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "events", func(t *schema.Blueprint) { + t.ID() + t.RawColumn("payload", "MEDIUMBLOB") + t.String("source", 100) + })).To(Succeed()) + + has, err := builder.HasColumn(c, "events", "payload") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates TEMPORARY table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp_import", func(t *schema.Blueprint) { + t.ID() + t.String("payload", 500) + t.Temporary() + })).To(Succeed()) + }) + + It("creates table with AutoIncrementStartingValues", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "invoices", func(t *schema.Blueprint) { + t.ID() + t.String("ref", 50) + t.AutoIncrementStartingValues(1000) + })).To(Succeed()) + }) + + It("creates table with Datetimes helper", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "logs", func(t *schema.Blueprint) { + t.ID() + t.Text("message") + t.Datetimes() + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "logs", []string{"created_at", "updated_at"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("returns error for duplicate table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + })).To(HaveOccurred()) + }) + }) + + Describe("Table (Alter)", func() { + It("adds columns to existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("phone", 20).Nullable() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "phone") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds column with After positioning", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("name", 255).After("id") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds invisible column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("internal_code", 50).Nullable().Invisible() + })).To(Succeed()) + }) + + It("adds column with charset and collation modifiers", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("bio", 1000).Nullable().Charset("utf8mb4").Collation("utf8mb4_unicode_ci") + })).To(Succeed()) + }) + + It("adds Timestamp column with OnUpdate", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.Timestamp("updated_at").Nullable().UseCurrentOnUpdate() + })).To(Succeed()) + }) + + It("drops SoftDeletes column via DropSoftDeletes", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "articles", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + Expect(builder.Table(c, "articles", func(t *schema.Blueprint) { + t.DropSoftDeletes() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "articles", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("drops column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("bio", 1000).Nullable() + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropColumn("bio") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "bio") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("renames column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("full_name", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.RenameColumn("full_name", "name") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds and drops index", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.Index("email").Name("idx_users_email") + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropIndex("idx_users_email") + })).To(Succeed()) + }) + + It("creates SpatialIndex on geometry column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "locations", func(t *schema.Blueprint) { + t.ID() + t.Geometry("coords", "", 0) + })).To(Succeed()) + + Expect(builder.Table(c, "locations", func(t *schema.Blueprint) { + t.SpatialIndex("coords") + })).To(Succeed()) + }) + }) + + Describe("Drop / DropIfExists / Rename", func() { + It("drops existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Drop(c, "tmp")).To(Succeed()) + + has, err := builder.HasTable(c, "tmp") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("returns error when dropping non-existent table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Drop(c, "no_such_table")).To(HaveOccurred()) + }) + + It("DropIfExists does not error for missing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.DropIfExists(c, "never_existed")).To(Succeed()) + }) + + It("renames an existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "old_name", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Rename(c, "old_name", "new_name")).To(Succeed()) + + has, err := builder.HasTable(c, "new_name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + }) + + Describe("Introspection", func() { + It("HasTable, HasColumn, HasColumns, HasIndex work correctly", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255).Unique("uk_users_email") + t.String("name", 255).Nullable() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasTable(c, "no_table") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + has, err = builder.HasColumn(c, "users", "email") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasColumn(c, "users", "phone") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + has, err = builder.HasColumns(c, "users", []string{"email", "name"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasIndex(c, "users", []string{"email"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("GetColumns returns column metadata", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255).Unique() + t.String("password", 255).Nullable() + t.Timestamps() + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(cols).To(HaveLen(6)) + Expect(colNames(cols)).To(ContainElements("id", "name", "email", "password", "created_at", "updated_at")) + }) + + It("GetTables lists all tables", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "table_a", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Create(c, "table_b", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + + tables, err := builder.GetTables(c) + Expect(err).NotTo(HaveOccurred()) + names := make([]string, len(tables)) + for i, tb := range tables { + names[i] = tb.Name + } + Expect(names).To(ContainElements("table_a", "table_b")) + }) + }) +}) diff --git a/integration/postgres_test.go b/integration/postgres_test.go new file mode 100644 index 0000000..caaa0fd --- /dev/null +++ b/integration/postgres_test.go @@ -0,0 +1,686 @@ +package integration_test + +import ( + "context" + "database/sql" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // Ginkgo specs use dot imports by convention. + . "github.com/onsi/gomega" //nolint:revive // Ginkgo specs use dot imports by convention. + "github.com/testcontainers/testcontainers-go" + + "github.com/akfaiz/migris/internal/testutil" + "github.com/akfaiz/migris/schema" +) + +var _ = Describe("PostgreSQL", Ordered, func() { + var ( + gCtx context.Context + db *sql.DB + tc testcontainers.Container + builder schema.Builder + ) + + BeforeAll(func() { + var err error + gCtx = context.Background() + tc, db, err = testutil.StartPostgresTestDB(gCtx) + Expect(err).NotTo(HaveOccurred()) + builder, err = schema.NewBuilder("postgres") + Expect(err).NotTo(HaveOccurred()) + }) + + AfterAll(func() { + _ = db.Close() + if tc != nil { + _ = tc.Terminate(gCtx) + } + }) + + // newCtx opens a fresh transaction. Callers must defer tx.Rollback(). + // Postgres DDL is transactional, so rollback cleans up created tables. + newCtx := func() (schema.Context, *sql.Tx) { + tx, err := db.BeginTx(gCtx, nil) + Expect(err).NotTo(HaveOccurred()) + return schema.NewContext(gCtx, tx), tx + } + + Describe("Create", func() { + It("returns error for nil context", func() { + Expect(builder.Create(nil, "t", func(_ *schema.Blueprint) {})).To(HaveOccurred()) + }) + + It("returns error for empty table name", func() { + c, tx := newCtx() + defer tx.Rollback() + Expect(builder.Create(c, "", func(_ *schema.Blueprint) {})).To(HaveOccurred()) + }) + + It("returns error for nil blueprint", func() { + c, tx := newCtx() + defer tx.Rollback() + Expect(builder.Create(c, "t", nil)).To(HaveOccurred()) + }) + + It("creates table with common column types", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255).Unique() + t.String("password", 255).Nullable() + t.Boolean("active").Default(true) + t.Timestamps() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with composite primary key", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "user_roles", func(t *schema.Blueprint) { + t.Integer("user_id") + t.Integer("role_id") + t.Primary("user_id", "role_id") + })).To(Succeed()) + }) + + It("creates table with foreign key and CASCADE actions", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + })).To(Succeed()) + + Expect(builder.Create(c, "orders", func(t *schema.Blueprint) { + t.ID() + t.BigInteger("user_id") + t.Decimal("amount", 10, 2) + t.Foreign("user_id").References("id").On("users").OnDelete("CASCADE").OnUpdate("CASCADE") + })).To(Succeed()) + }) + + It("creates table with SoftDeletes", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + + cols, err := builder.GetColumns(c, "posts") + Expect(err).NotTo(HaveOccurred()) + var deletedAtCol *schema.Column + for _, col := range cols { + if col.Name == "deleted_at" { + deletedAtCol = col + break + } + } + Expect(deletedAtCol).NotTo(BeNil()) + Expect(deletedAtCol.Nullable).To(BeTrue()) + }) + + It("creates table with SoftDeletesTz", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "events", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.SoftDeletesTz("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "events", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with NumericMorphs columns and index", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "comments", func(t *schema.Blueprint) { + t.ID() + t.NumericMorphs("commentable") + t.Text("body") + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "comments", []string{"commentable_type", "commentable_id"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("creates table with UUIDMorphs", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "taggables", func(t *schema.Blueprint) { + t.ID() + t.UUIDMorphs("taggable") + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "taggables") + Expect(err).NotTo(HaveOccurred()) + Expect(colNames(cols)).To(ContainElements("taggable_type", "taggable_id")) + }) + + It("creates table with ForeignID.Constrained", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + })).To(Succeed()) + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.ForeignID("user_id").Constrained() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "user_id") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with RawColumn", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "events", func(t *schema.Blueprint) { + t.ID() + t.RawColumn("metadata", "JSONB") + t.String("name", 255) + })).To(Succeed()) + + has, err := builder.HasColumn(c, "events", "metadata") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates a TEMPORARY table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp_import", func(t *schema.Blueprint) { + t.ID() + t.String("payload", 500) + t.Temporary() + })).To(Succeed()) + }) + + It("creates table with Datetimes helper", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "logs", func(t *schema.Blueprint) { + t.ID() + t.Text("message") + t.Datetimes() + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "logs", []string{"created_at", "updated_at"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("creates table with NullableTimestamps", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "sessions", func(t *schema.Blueprint) { + t.ID() + t.String("token", 100) + t.NullableTimestamps() + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "sessions") + Expect(err).NotTo(HaveOccurred()) + for _, col := range cols { + if col.Name == "created_at" || col.Name == "updated_at" { + Expect(col.Nullable).To(BeTrue(), "expected %s to be nullable", col.Name) + } + } + }) + + It("returns error for duplicate table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + })).To(HaveOccurred()) + }) + + // VectorIndex requires pgvector extension not available in standard postgres:16-alpine. + PIt("creates table with VectorIndex (requires pgvector)", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "items", func(t *schema.Blueprint) { + t.ID() + t.Vector("embedding", 1536) + })).To(Succeed()) + + Expect(builder.Table(c, "items", func(t *schema.Blueprint) { + t.VectorIndex("embedding") + })).To(Succeed()) + }) + }) + + Describe("Table (Alter)", func() { + It("adds columns to existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("phone", 20).Nullable() + t.String("address", 255).Nullable() + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "users", []string{"phone", "address"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("adds SoftDeletes column via Table alter", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "articles", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "articles", func(t *schema.Blueprint) { + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "articles", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("drops SoftDeletes column via DropSoftDeletes", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "articles", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + Expect(builder.Table(c, "articles", func(t *schema.Blueprint) { + t.DropSoftDeletes() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "articles", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("modifies existing column with Change()", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 100) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("name", 255).Nullable().Change() + })).To(Succeed()) + }) + + It("drops column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("bio", 1000).Nullable() + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropColumn("bio") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "bio") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("renames column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("full_name", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.RenameColumn("full_name", "name") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds, renames, and drops index", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.Index("email").Name("idx_users_email") + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.RenameIndex("idx_users_email", "idx_users_contact") + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropIndex("idx_users_contact") + })).To(Succeed()) + + has, err := builder.HasIndex(c, "users", []string{"idx_users_contact"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("adds and drops foreign key", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "roles", func(t *schema.Blueprint) { + t.ID() + t.String("name", 100) + })).To(Succeed()) + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.Integer("role_id").Nullable() + t.Foreign("role_id").References("id").On("roles").OnDelete("SET NULL") + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropForeign([]string{"role_id"}) + })).To(Succeed()) + }) + }) + + Describe("Drop", func() { + It("drops an existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + Expect(builder.Drop(c, "tmp")).To(Succeed()) + + has, err := builder.HasTable(c, "tmp") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("returns error when dropping non-existent table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Drop(c, "no_such_table")).To(HaveOccurred()) + }) + }) + + Describe("DropIfExists", func() { + It("drops existing table without error", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + Expect(builder.DropIfExists(c, "tmp")).To(Succeed()) + }) + + It("does not error when table does not exist", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.DropIfExists(c, "never_existed")).To(Succeed()) + }) + }) + + Describe("Rename", func() { + It("renames an existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "old_name", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + Expect(builder.Rename(c, "old_name", "new_name")).To(Succeed()) + + has, err := builder.HasTable(c, "new_name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasTable(c, "old_name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("returns error for non-existent source table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Rename(c, "ghost", "phantom")).To(HaveOccurred()) + }) + }) + + Describe("Introspection", func() { + Describe("HasTable", func() { + It("returns true for existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("returns false for non-existent table", func() { + c, tx := newCtx() + defer tx.Rollback() + + has, err := builder.HasTable(c, "no_such_table") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + }) + + Describe("HasColumn / HasColumns", func() { + It("returns true for existing column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "email") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("returns false for non-existent column", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "phone") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("returns true when all columns exist", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255) + })).To(Succeed()) + + has, err := builder.HasColumns(c, "users", []string{"name", "email"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + }) + + Describe("HasIndex", func() { + It("detects index by column list", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255).Unique("uk_users_email") + })).To(Succeed()) + + has, err := builder.HasIndex(c, "users", []string{"email"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("returns false for non-existent index", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + has, err := builder.HasIndex(c, "users", []string{"email"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + }) + + Describe("GetColumns", func() { + It("returns all columns with metadata", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255).Unique() + t.String("password", 255).Nullable() + t.Timestamps() + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(cols).To(HaveLen(6)) + Expect(colNames(cols)).To(ContainElements( + "id", "name", "email", "password", "created_at", "updated_at", + )) + }) + + It("returns empty slice for non-existent table", func() { + c, tx := newCtx() + defer tx.Rollback() + + cols, err := builder.GetColumns(c, "ghost") + Expect(err).NotTo(HaveOccurred()) + Expect(cols).To(BeEmpty()) + }) + }) + + Describe("GetIndexes", func() { + It("returns all indexes including primary", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255).Unique("uk_users_email") + t.Index("email").Name("idx_users_email") + })).To(Succeed()) + + idxs, err := builder.GetIndexes(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(idxs).NotTo(BeEmpty()) + Expect(indexNames(idxs)).To(ContainElement("uk_users_email")) + }) + }) + + Describe("GetTables", func() { + It("lists created tables", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "alpha", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Create(c, "beta", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + + tables, err := builder.GetTables(c) + Expect(err).NotTo(HaveOccurred()) + names := make([]string, len(tables)) + for i, tb := range tables { + names[i] = tb.Name + } + Expect(names).To(ContainElements("alpha", "beta")) + }) + }) + }) +}) diff --git a/integration/sqlite_test.go b/integration/sqlite_test.go new file mode 100644 index 0000000..b712848 --- /dev/null +++ b/integration/sqlite_test.go @@ -0,0 +1,335 @@ +package integration_test + +import ( + "context" + "database/sql" + "os" + + _ "github.com/mattn/go-sqlite3" + . "github.com/onsi/ginkgo/v2" //nolint:revive // Ginkgo specs use dot imports by convention. + . "github.com/onsi/gomega" //nolint:revive // Ginkgo specs use dot imports by convention. + + "github.com/akfaiz/migris/schema" +) + +var _ = Describe("SQLite", Ordered, func() { + const dbFile = "test_integration_sqlite.db" + + var ( + sCtx context.Context + db *sql.DB + builder schema.Builder + ) + + BeforeAll(func() { + var err error + sCtx = context.Background() + db, err = sql.Open("sqlite3", dbFile) + Expect(err).NotTo(HaveOccurred()) + Expect(db.Ping()).To(Succeed()) + builder, err = schema.NewBuilder("sqlite3") + Expect(err).NotTo(HaveOccurred()) + }) + + AfterAll(func() { + _ = db.Close() + _ = os.Remove(dbFile) + }) + + // dropAll removes all tables after each spec for isolation. + dropAll := func() { + tx, err := db.BeginTx(sCtx, nil) + if err != nil { + return + } + c := schema.NewContext(sCtx, tx) + tables, err := builder.GetTables(c) + if err != nil { + _ = tx.Rollback() + return + } + for _, t := range tables { + _ = builder.DropIfExists(c, t.Name) + } + _ = tx.Commit() + } + + newCtx := func() (schema.Context, *sql.Tx) { + tx, err := db.BeginTx(sCtx, nil) + Expect(err).NotTo(HaveOccurred()) + return schema.NewContext(sCtx, tx), tx + } + + AfterEach(func() { + dropAll() + }) + + Describe("Create", func() { + It("returns error for nil context", func() { + Expect(builder.Create(nil, "t", func(_ *schema.Blueprint) {})).To(HaveOccurred()) + }) + + It("returns error for empty table name", func() { + c, tx := newCtx() + defer tx.Rollback() + Expect(builder.Create(c, "", func(_ *schema.Blueprint) {})).To(HaveOccurred()) + }) + + It("returns error for nil blueprint", func() { + c, tx := newCtx() + defer tx.Rollback() + Expect(builder.Create(c, "t", nil)).To(HaveOccurred()) + }) + + It("creates table with common column types", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255).Unique() + t.String("password", 255).Nullable() + t.Boolean("active").Default(true) + t.Timestamps() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with SoftDeletes", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "posts", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "posts", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with NumericMorphs", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "comments", func(t *schema.Blueprint) { + t.ID() + t.NumericMorphs("commentable") + t.Text("body") + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "comments", []string{"commentable_type", "commentable_id"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("creates table with RawColumn", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "events", func(t *schema.Blueprint) { + t.ID() + t.RawColumn("payload", "BLOB") + t.String("source", 100) + })).To(Succeed()) + + has, err := builder.HasColumn(c, "events", "payload") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("creates table with Datetimes helper", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "logs", func(t *schema.Blueprint) { + t.ID() + t.Text("message") + t.Datetimes() + })).To(Succeed()) + + hasC, errC := builder.HasColumns(c, "logs", []string{"created_at", "updated_at"}) + Expect(errC).NotTo(HaveOccurred()) + Expect(hasC).To(BeTrue()) + }) + + It("creates table with composite primary key", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "user_roles", func(t *schema.Blueprint) { + t.Integer("user_id") + t.Integer("role_id") + t.Primary("user_id", "role_id") + })).To(Succeed()) + }) + + It("returns error for duplicate table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { t.ID() })).To(HaveOccurred()) + }) + }) + + Describe("Table (Alter)", func() { + It("adds new columns", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.String("phone", 20).Nullable() + })).To(Succeed()) + + has, err := builder.HasColumn(c, "users", "phone") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds SoftDeletes to existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "articles", func(t *schema.Blueprint) { + t.ID() + t.String("title", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "articles", func(t *schema.Blueprint) { + t.SoftDeletes("deleted_at") + })).To(Succeed()) + + has, err := builder.HasColumn(c, "articles", "deleted_at") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("adds and drops unique index", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255) + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.Unique("email").Name("uk_users_email") + })).To(Succeed()) + + Expect(builder.Table(c, "users", func(t *schema.Blueprint) { + t.DropIndex("uk_users_email") + })).To(Succeed()) + }) + }) + + Describe("Drop / DropIfExists / Rename", func() { + It("drops existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "tmp", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Drop(c, "tmp")).To(Succeed()) + + has, err := builder.HasTable(c, "tmp") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + + It("DropIfExists does not error for missing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.DropIfExists(c, "never_existed")).To(Succeed()) + }) + + It("renames an existing table", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "old_name", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Rename(c, "old_name", "new_name")).To(Succeed()) + + has, err := builder.HasTable(c, "new_name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasTable(c, "old_name") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + }) + }) + + Describe("Introspection", func() { + It("HasTable / HasColumn / HasColumns / HasIndex", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("email", 255).Unique("uk_users_email") + t.String("name", 255).Nullable() + })).To(Succeed()) + + has, err := builder.HasTable(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasTable(c, "no_table") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeFalse()) + has, err = builder.HasColumn(c, "users", "email") + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasColumns(c, "users", []string{"email", "name"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + has, err = builder.HasIndex(c, "users", []string{"email"}) + Expect(err).NotTo(HaveOccurred()) + Expect(has).To(BeTrue()) + }) + + It("GetColumns returns all columns", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "users", func(t *schema.Blueprint) { + t.ID() + t.String("name", 255) + t.String("email", 255) + t.Timestamps() + })).To(Succeed()) + + cols, err := builder.GetColumns(c, "users") + Expect(err).NotTo(HaveOccurred()) + Expect(cols).To(HaveLen(5)) + Expect(colNames(cols)).To(ContainElements("id", "name", "email", "created_at", "updated_at")) + }) + + It("GetTables lists all tables", func() { + c, tx := newCtx() + defer tx.Rollback() + + Expect(builder.Create(c, "alpha", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + Expect(builder.Create(c, "beta", func(t *schema.Blueprint) { t.ID() })).To(Succeed()) + + tables, err := builder.GetTables(c) + Expect(err).NotTo(HaveOccurred()) + names := make([]string, len(tables)) + for i, tb := range tables { + names[i] = tb.Name + } + Expect(names).To(ContainElements("alpha", "beta")) + }) + }) +}) diff --git a/integration/suite_test.go b/integration/suite_test.go new file mode 100644 index 0000000..8eb90f7 --- /dev/null +++ b/integration/suite_test.go @@ -0,0 +1,13 @@ +package integration_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // Ginkgo specs use dot imports by convention. + . "github.com/onsi/gomega" //nolint:revive // Ginkgo specs use dot imports by convention. +) + +func TestIntegration(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Integration Suite") +} From 265981220b74a0b7b6748d22fa4d8ed2332c1921 Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 22:58:46 +0700 Subject: [PATCH 4/7] chore: sync extra module dependencies --- .gitignore | 1 + extra/migriscli/go.mod | 10 +-- extra/migriscli/go.sum | 176 ++++++++++++++++++++------------------- extra/migriscobra/go.mod | 10 +-- extra/migriscobra/go.sum | 176 ++++++++++++++++++++------------------- 5 files changed, 191 insertions(+), 182 deletions(-) diff --git a/.gitignore b/.gitignore index 8fa4bf4..78e931f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ go.work.sum .vscode/ .serena/ .claude/ +references/ diff --git a/extra/migriscli/go.mod b/extra/migriscli/go.mod index 78c81ea..ce35d37 100644 --- a/extra/migriscli/go.mod +++ b/extra/migriscli/go.mod @@ -1,6 +1,6 @@ module github.com/akfaiz/migris/extra/migriscli -go 1.23.0 +go 1.25.0 require ( github.com/akfaiz/migris v0.4.1 @@ -12,12 +12,12 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/pressly/goose/v3 v3.26.0 // indirect + github.com/pressly/goose/v3 v3.27.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect ) replace github.com/akfaiz/migris => ../.. diff --git a/extra/migriscli/go.sum b/extra/migriscli/go.sum index d466d61..5124243 100644 --- a/extra/migriscli/go.sum +++ b/extra/migriscli/go.sum @@ -1,13 +1,19 @@ -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -18,16 +24,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -36,28 +40,26 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -69,91 +71,93 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= -github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= +github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= -github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= +github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 h1:JrBOApOZ25GOkA+wX4YW4+MxCZ4VB62TC41CFyOi+LI= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0/go.mod h1:6A8KkwZ+iOinzhyzvg3C082Xk0ihFyLDQads38fM07M= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 h1:WUYu/gB23et+T9XCqfmYJB7eWW8/1qqPCxNIaRbS0yk= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0/go.mod h1:ED7dKWk3JE/dMRJ3t45TNGf3h9/htQG5au5gd1DuQcw= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 h1:xTGNNsOD9IIssH0dnAGNUH+SD9GYWyaP2t5xD2lg0as= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0/go.mod h1:WKS3MGq1lzbVibIRnL08TOaf5bKWPxJe5frzyQfV4oY= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo= github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= -modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= +modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= -modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= diff --git a/extra/migriscobra/go.mod b/extra/migriscobra/go.mod index 935ae9a..51c5e15 100644 --- a/extra/migriscobra/go.mod +++ b/extra/migriscobra/go.mod @@ -1,6 +1,6 @@ module github.com/akfaiz/migris/extra/migriscobra -go 1.23.0 +go 1.25.0 require ( github.com/akfaiz/migris v0.4.1 @@ -13,13 +13,13 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/pressly/goose/v3 v3.26.0 // indirect + github.com/pressly/goose/v3 v3.27.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect ) replace github.com/akfaiz/migris => ../.. diff --git a/extra/migriscobra/go.sum b/extra/migriscobra/go.sum index 7a9dfb8..6e7b368 100644 --- a/extra/migriscobra/go.sum +++ b/extra/migriscobra/go.sum @@ -1,13 +1,19 @@ -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -19,16 +25,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -37,12 +41,10 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -51,16 +53,16 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -72,95 +74,97 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= -github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= +github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= -github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= +github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 h1:JrBOApOZ25GOkA+wX4YW4+MxCZ4VB62TC41CFyOi+LI= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0/go.mod h1:6A8KkwZ+iOinzhyzvg3C082Xk0ihFyLDQads38fM07M= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 h1:WUYu/gB23et+T9XCqfmYJB7eWW8/1qqPCxNIaRbS0yk= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0/go.mod h1:ED7dKWk3JE/dMRJ3t45TNGf3h9/htQG5au5gd1DuQcw= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 h1:xTGNNsOD9IIssH0dnAGNUH+SD9GYWyaP2t5xD2lg0as= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0/go.mod h1:WKS3MGq1lzbVibIRnL08TOaf5bKWPxJe5frzyQfV4oY= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= -modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= +modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= -modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= From a1bdb92318bd40f04f3179675a30326e9b017874 Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 23:00:19 +0700 Subject: [PATCH 5/7] chore: sync example dependencies --- examples/basic/go.mod | 15 ++- examples/basic/go.sum | 176 ++++++++++++++++--------------- extra/migriscli/example/go.mod | 15 ++- extra/migriscli/example/go.sum | 176 ++++++++++++++++--------------- extra/migriscobra/example/go.mod | 15 ++- extra/migriscobra/example/go.sum | 176 ++++++++++++++++--------------- 6 files changed, 291 insertions(+), 282 deletions(-) diff --git a/examples/basic/go.mod b/examples/basic/go.mod index b3cfe63..b78e7f2 100644 --- a/examples/basic/go.mod +++ b/examples/basic/go.mod @@ -1,10 +1,10 @@ module github.com/akfaiz/migris/examples/basic -go 1.23.0 +go 1.25.0 require ( github.com/akfaiz/migris v0.0.0 - github.com/jackc/pgx/v5 v5.7.5 + github.com/jackc/pgx/v5 v5.8.0 github.com/joho/godotenv v1.5.1 ) @@ -16,14 +16,13 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/pressly/goose/v3 v3.26.0 // indirect + github.com/pressly/goose/v3 v3.27.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect ) replace github.com/akfaiz/migris => ../.. diff --git a/examples/basic/go.sum b/examples/basic/go.sum index 8378ab3..9ab35c1 100644 --- a/examples/basic/go.sum +++ b/examples/basic/go.sum @@ -1,13 +1,19 @@ -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -19,16 +25,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -37,30 +41,28 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -72,94 +74,96 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= -github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= +github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= -github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= +github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 h1:JrBOApOZ25GOkA+wX4YW4+MxCZ4VB62TC41CFyOi+LI= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0/go.mod h1:6A8KkwZ+iOinzhyzvg3C082Xk0ihFyLDQads38fM07M= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 h1:WUYu/gB23et+T9XCqfmYJB7eWW8/1qqPCxNIaRbS0yk= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0/go.mod h1:ED7dKWk3JE/dMRJ3t45TNGf3h9/htQG5au5gd1DuQcw= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 h1:xTGNNsOD9IIssH0dnAGNUH+SD9GYWyaP2t5xD2lg0as= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0/go.mod h1:WKS3MGq1lzbVibIRnL08TOaf5bKWPxJe5frzyQfV4oY= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= -modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= +modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= -modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= diff --git a/extra/migriscli/example/go.mod b/extra/migriscli/example/go.mod index f2bcfe4..ec36c86 100644 --- a/extra/migriscli/example/go.mod +++ b/extra/migriscli/example/go.mod @@ -1,11 +1,11 @@ module github.com/akfaiz/migris/extra/migriscli/example -go 1.23.0 +go 1.25.0 require ( github.com/akfaiz/migris v0.4.1 github.com/akfaiz/migris/extra/migriscli v0.0.0 - github.com/jackc/pgx/v5 v5.7.5 + github.com/jackc/pgx/v5 v5.8.0 github.com/joho/godotenv v1.5.1 ) @@ -17,15 +17,14 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/pressly/goose/v3 v3.26.0 // indirect + github.com/pressly/goose/v3 v3.27.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/urfave/cli/v3 v3.6.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect ) replace github.com/akfaiz/migris => ../../.. diff --git a/extra/migriscli/example/go.sum b/extra/migriscli/example/go.sum index 5b0aa3d..7c6a1aa 100644 --- a/extra/migriscli/example/go.sum +++ b/extra/migriscli/example/go.sum @@ -1,13 +1,19 @@ -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -19,16 +25,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -37,30 +41,28 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -72,96 +74,98 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= -github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= +github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= -github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= +github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 h1:JrBOApOZ25GOkA+wX4YW4+MxCZ4VB62TC41CFyOi+LI= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0/go.mod h1:6A8KkwZ+iOinzhyzvg3C082Xk0ihFyLDQads38fM07M= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 h1:WUYu/gB23et+T9XCqfmYJB7eWW8/1qqPCxNIaRbS0yk= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0/go.mod h1:ED7dKWk3JE/dMRJ3t45TNGf3h9/htQG5au5gd1DuQcw= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 h1:xTGNNsOD9IIssH0dnAGNUH+SD9GYWyaP2t5xD2lg0as= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0/go.mod h1:WKS3MGq1lzbVibIRnL08TOaf5bKWPxJe5frzyQfV4oY= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo= github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= -modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= +modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= -modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= diff --git a/extra/migriscobra/example/go.mod b/extra/migriscobra/example/go.mod index 9aa19fe..45d8962 100644 --- a/extra/migriscobra/example/go.mod +++ b/extra/migriscobra/example/go.mod @@ -1,11 +1,11 @@ module github.com/akfaiz/migris/extra/migriscobra/example -go 1.23.0 +go 1.25.0 require ( github.com/akfaiz/migris v0.4.1 github.com/akfaiz/migris/extra/migriscobra v0.0.0 - github.com/jackc/pgx/v5 v5.7.5 + github.com/jackc/pgx/v5 v5.8.0 github.com/joho/godotenv v1.5.1 ) @@ -18,16 +18,15 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/pressly/goose/v3 v3.26.0 // indirect + github.com/pressly/goose/v3 v3.27.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect ) replace github.com/akfaiz/migris => ../../.. diff --git a/extra/migriscobra/example/go.sum b/extra/migriscobra/example/go.sum index 780ec4a..a942b78 100644 --- a/extra/migriscobra/example/go.sum +++ b/extra/migriscobra/example/go.sum @@ -1,13 +1,19 @@ -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= @@ -20,16 +26,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= -github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -38,12 +42,10 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -52,18 +54,18 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= -github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e h1:Q6MvJtQK/iRcRtzAscm/zF23XxJlbECiGPyRicsX+Ak= +github.com/lufia/plan9stats v0.0.0-20260330125221-c963978e514e/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -75,41 +77,43 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= -github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= +github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs= -github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY= +github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -119,55 +123,55 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00= -github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0 h1:JrBOApOZ25GOkA+wX4YW4+MxCZ4VB62TC41CFyOi+LI= -github.com/testcontainers/testcontainers-go/modules/mariadb v0.36.0/go.mod h1:6A8KkwZ+iOinzhyzvg3C082Xk0ihFyLDQads38fM07M= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0 h1:WUYu/gB23et+T9XCqfmYJB7eWW8/1qqPCxNIaRbS0yk= -github.com/testcontainers/testcontainers-go/modules/mysql v0.36.0/go.mod h1:ED7dKWk3JE/dMRJ3t45TNGf3h9/htQG5au5gd1DuQcw= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0 h1:xTGNNsOD9IIssH0dnAGNUH+SD9GYWyaP2t5xD2lg0as= -github.com/testcontainers/testcontainers-go/modules/postgres v0.36.0/go.mod h1:WKS3MGq1lzbVibIRnL08TOaf5bKWPxJe5frzyQfV4oY= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0 h1:GCbb1ndrF7OTDiIvxXyItaDab4qkzTFJ48LKFdM7EIo= +github.com/testcontainers/testcontainers-go/modules/postgres v0.42.0/go.mod h1:IRPBaI8jXdrNfD0e4Zm7Fbcgaz5shKxOQv4axiL09xs= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ= -modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8= +modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= +modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek= -modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= +modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= +modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= From ba5698d8c018976faa51de56095269075183a60e Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 23:09:50 +0700 Subject: [PATCH 6/7] test: add unit test at extra module --- extra/migriscli/migriscli_test.go | 150 ++++++++++++++++++++++++ extra/migriscobra/migriscobra_test.go | 160 ++++++++++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 extra/migriscli/migriscli_test.go create mode 100644 extra/migriscobra/migriscobra_test.go diff --git a/extra/migriscli/migriscli_test.go b/extra/migriscli/migriscli_test.go new file mode 100644 index 0000000..243442c --- /dev/null +++ b/extra/migriscli/migriscli_test.go @@ -0,0 +1,150 @@ +package migriscli //nolint:testpackage // Tests cover unexported command builders and option helpers. + +import ( + "bytes" + "context" + "os" + "slices" + "strings" + "testing" + + "github.com/urfave/cli/v3" +) + +func TestNewCLI(t *testing.T) { + cmd := NewCLI(Config{MigrationsDir: "migrations"}) + + if cmd.Name != "migrate" { + t.Fatalf("expected command name migrate, got %q", cmd.Name) + } + if cmd.Usage != "Database migration CLI tool" { + t.Fatalf("unexpected command usage: %q", cmd.Usage) + } + if len(cmd.Commands) != 7 { + t.Fatalf("expected 7 commands, got %d", len(cmd.Commands)) + } + wantCommands := []string{"create", "up", "up-to", "down", "down-to", "reset", "status"} + if !slices.Equal(commandNames(cmd.Commands), wantCommands) { + t.Fatalf("expected commands %v, got %v", wantCommands, commandNames(cmd.Commands)) + } + + createCmd := cmd.Command("create") + if createCmd == nil { + t.Fatal("expected create command") + } + if len(createCmd.Flags) != 1 { + t.Fatalf("expected 1 create flag, got %d", len(createCmd.Flags)) + } + nameFlag, ok := createCmd.Flags[0].(*cli.StringFlag) + if !ok { + t.Fatalf("expected create flag to be *cli.StringFlag, got %T", createCmd.Flags[0]) + } + if nameFlag.Name != "name" || !slices.Equal(nameFlag.Aliases, []string{"n"}) || !nameFlag.Required { + t.Fatalf("unexpected create name flag: %#v", nameFlag) + } + + upToCmd := cmd.Command("up-to") + if upToCmd == nil { + t.Fatal("expected up-to command") + } + if len(upToCmd.Flags) != 2 { + t.Fatalf("expected 2 up-to flags, got %d", len(upToCmd.Flags)) + } + versionFlag, ok := upToCmd.Flags[1].(*cli.Int64Flag) + if !ok { + t.Fatalf("expected version flag to be *cli.Int64Flag, got %T", upToCmd.Flags[1]) + } + if versionFlag.Name != "version" || !slices.Equal(versionFlag.Aliases, []string{"v"}) || !versionFlag.Required { + t.Fatalf("unexpected up-to version flag: %#v", versionFlag) + } +} + +func TestCreateCommandCreatesMigrationFile(t *testing.T) { + dir := t.TempDir() + cmd := NewCLI(Config{MigrationsDir: dir}) + cmd.Writer = bytes.NewBuffer(nil) + cmd.ErrWriter = bytes.NewBuffer(nil) + + err := cmd.Run(context.Background(), []string{"migrate", "create", "--name", "create_users_table"}) + if err != nil { + t.Fatalf("expected create command to succeed: %v", err) + } + + entries, err := os.ReadDir(dir) + if err != nil { + t.Fatalf("expected to read migration dir: %v", err) + } + if len(entries) != 1 { + t.Fatalf("expected 1 migration file, got %d", len(entries)) + } + if !strings.Contains(entries[0].Name(), "_create_users_table.go") { + t.Fatalf("expected generated create users migration, got %q", entries[0].Name()) + } +} + +func TestMigrationCommandsReturnMigratorConfigErrors(t *testing.T) { + for _, name := range []string{"up", "up-to", "down", "down-to", "reset", "status"} { + t.Run(name, func(t *testing.T) { + args := []string{"migrate", name} + if name == "up-to" || name == "down-to" { + args = append(args, "--version", "20250101000000") + } + + cmd := NewCLI(Config{Dialect: "unknown", MigrationsDir: t.TempDir()}) + cmd.Writer = bytes.NewBuffer(nil) + cmd.ErrWriter = bytes.NewBuffer(nil) + + err := cmd.Run(context.Background(), args) + if err == nil { + t.Fatal("expected migrator config error") + } + if !strings.Contains(err.Error(), "unknown database dialect") { + t.Fatalf("expected unknown dialect error, got %v", err) + } + }) + } +} + +func TestRunOpts(t *testing.T) { + var got []any + cmd := runOptsTestCommand(&got, Config{AllowMissing: true}) + + err := cmd.Run(context.Background(), []string{"up", "--dry-run"}) + if err != nil { + t.Fatalf("expected dry-run command to succeed: %v", err) + } + if len(got) != 2 { + t.Fatalf("expected 2 run options, got %d", len(got)) + } + + got = nil + cmd = runOptsTestCommand(&got, Config{}) + err = cmd.Run(context.Background(), []string{"up"}) + if err != nil { + t.Fatalf("expected command to succeed: %v", err) + } + if len(got) != 0 { + t.Fatalf("expected no run options, got %d", len(got)) + } +} + +func runOptsTestCommand(got *[]any, cfg Config) *cli.Command { + return &cli.Command{ + Name: "up", + Flags: []cli.Flag{dryRunFlag()}, + Action: func(_ context.Context, c *cli.Command) error { + for _, opt := range runOpts(c, cfg) { + *got = append(*got, opt) + } + return nil + }, + } +} + +func commandNames(commands []*cli.Command) []string { + names := make([]string, len(commands)) + for i, cmd := range commands { + names[i] = cmd.Name + } + return names +} diff --git a/extra/migriscobra/migriscobra_test.go b/extra/migriscobra/migriscobra_test.go new file mode 100644 index 0000000..fc09adb --- /dev/null +++ b/extra/migriscobra/migriscobra_test.go @@ -0,0 +1,160 @@ +package migriscobra //nolint:testpackage // Tests cover unexported command builders and option helpers. + +import ( + "bytes" + "os" + "slices" + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func TestNewCLI(t *testing.T) { + cmd := NewCLI(Config{MigrationsDir: "migrations"}) + + if cmd.Use != "migrate" { + t.Fatalf("expected command use migrate, got %q", cmd.Use) + } + if cmd.Short != "Database migration CLI tool" { + t.Fatalf("unexpected command short help: %q", cmd.Short) + } + if cmd.Long != "A powerful database migration tool powered by migris" { + t.Fatalf("unexpected command long help: %q", cmd.Long) + } + wantCommands := []string{"create", "up", "up-to", "down", "down-to", "reset", "status"} + gotCommands := cobraCommandNames(cmd.Commands()) + slices.Sort(wantCommands) + slices.Sort(gotCommands) + if !slices.Equal(gotCommands, wantCommands) { + t.Fatalf("expected commands %v, got %v", wantCommands, gotCommands) + } + + createCmd, _, err := cmd.Find([]string{"create"}) + if err != nil { + t.Fatalf("expected to find create command: %v", err) + } + if createCmd == nil { + t.Fatal("expected create command") + } + nameFlag := createCmd.Flags().Lookup("name") + if nameFlag == nil { + t.Fatal("expected create name flag") + } + if nameFlag.Shorthand != "n" || + !slices.Contains(nameFlag.Annotations["cobra_annotation_bash_completion_one_required_flag"], "true") { + t.Fatalf("unexpected create name flag: %#v", nameFlag) + } + + upToCmd, _, err := cmd.Find([]string{"up-to"}) + if err != nil { + t.Fatalf("expected to find up-to command: %v", err) + } + if upToCmd == nil { + t.Fatal("expected up-to command") + } + versionFlag := upToCmd.Flags().Lookup("version") + if versionFlag == nil { + t.Fatal("expected up-to version flag") + } + if versionFlag.Shorthand != "v" || + !slices.Contains(versionFlag.Annotations["cobra_annotation_bash_completion_one_required_flag"], "true") { + t.Fatalf("unexpected up-to version flag: %#v", versionFlag) + } +} + +func TestCreateCommandCreatesMigrationFile(t *testing.T) { + dir := t.TempDir() + cmd := NewCLI(Config{MigrationsDir: dir}) + cmd.SetOut(bytes.NewBuffer(nil)) + cmd.SetErr(bytes.NewBuffer(nil)) + cmd.SetArgs([]string{"create", "--name", "create_users_table"}) + + err := cmd.Execute() + if err != nil { + t.Fatalf("expected create command to succeed: %v", err) + } + + entries, err := os.ReadDir(dir) + if err != nil { + t.Fatalf("expected to read migration dir: %v", err) + } + if len(entries) != 1 { + t.Fatalf("expected 1 migration file, got %d", len(entries)) + } + if !strings.Contains(entries[0].Name(), "_create_users_table.go") { + t.Fatalf("expected generated create users migration, got %q", entries[0].Name()) + } +} + +func TestMigrationCommandsReturnMigratorConfigErrors(t *testing.T) { + for _, name := range []string{"up", "up-to", "down", "down-to", "reset", "status"} { + t.Run(name, func(t *testing.T) { + args := []string{name} + if name == "up-to" || name == "down-to" { + args = append(args, "--version", "20250101000000") + } + + cmd := NewCLI(Config{Dialect: "unknown", MigrationsDir: t.TempDir()}) + cmd.SetOut(bytes.NewBuffer(nil)) + cmd.SetErr(bytes.NewBuffer(nil)) + cmd.SetArgs(args) + + err := cmd.Execute() + if err == nil { + t.Fatal("expected migrator config error") + } + if !strings.Contains(err.Error(), "unknown database dialect") { + t.Fatalf("expected unknown dialect error, got %v", err) + } + }) + } +} + +func TestRequiredFlags(t *testing.T) { + for _, args := range [][]string{{"create"}, {"up-to"}, {"down-to"}} { + t.Run(args[0], func(t *testing.T) { + cmd := NewCLI(Config{MigrationsDir: t.TempDir()}) + cmd.SetOut(bytes.NewBuffer(nil)) + cmd.SetErr(bytes.NewBuffer(nil)) + cmd.SetArgs(args) + + err := cmd.Execute() + if err == nil { + t.Fatal("expected required flag error") + } + if !strings.Contains(err.Error(), "required flag") { + t.Fatalf("expected required flag error, got %v", err) + } + }) + } +} + +func TestRunOpts(t *testing.T) { + cmd := createUpCommand(Config{}) + if err := cmd.Flags().Set("dry-run", "true"); err != nil { + t.Fatalf("expected to set dry-run flag: %v", err) + } + + opts := runOpts(cmd, Config{AllowMissing: true}) + if len(opts) != 2 { + t.Fatalf("expected 2 run options, got %d", len(opts)) + } + + cmd = createUpCommand(Config{}) + opts = runOpts(cmd, Config{}) + if len(opts) != 0 { + t.Fatalf("expected no run options, got %d", len(opts)) + } +} + +func cobraCommandNames(commands []*cobra.Command) []string { + names := make([]string, 0, len(commands)) + for _, cmd := range commands { + if cmd.Use == "help [command]" { + continue + } + names = append(names, cmd.Use) + } + return names +} From 41781328bae12efb26c643b7ab98aa5dacd4b87e Mon Sep 17 00:00:00 2001 From: Ahmad Faiz Kamaludin Date: Sat, 23 May 2026 23:12:42 +0700 Subject: [PATCH 7/7] ci: update test matrix version --- .github/workflows/ci.yml | 2 +- lefthook.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7f25df..5e42d5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: ['1.23', '1.24', '1.25'] + go-version: ['1.25', '1.26'] steps: - name: Checkout code diff --git a/lefthook.yml b/lefthook.yml index aebac70..19149de 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -17,7 +17,7 @@ commit-msg: commands: check-message: run: | - if ! grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+" {1}; then + if ! grep -qE "^(feat|fix|docs|style|refactor|test|chore|ci)(\(.+\))?: .+" {1}; then echo "Commit message must follow conventional commits format" echo "Example: feat: add new feature" exit 1