• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stephenafamo / bob / 16877516283

11 Aug 2025 10:31AM UTC coverage: 41.249% (+0.04%) from 41.214%
16877516283

push

github

stephenafamo
Move plugins setup from helpers to plugins package

12 of 18 new or added lines in 6 files covered. (66.67%)

2 existing lines in 1 file now uncovered.

9338 of 22638 relevant lines covered (41.25%)

278.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/gen/bobgen-sql/driver/sql.go
1
package driver
2

3
import (
4
        "context"
5
        "database/sql"
6
        "fmt"
7
        "io"
8
        "io/fs"
9
        "log"
10
        "os"
11

12
        "github.com/lib/pq"
13
        "github.com/stephenafamo/bob/gen"
14
        helpers "github.com/stephenafamo/bob/gen/bobgen-helpers"
15
        mysqlDriver "github.com/stephenafamo/bob/gen/bobgen-mysql/driver"
16
        psqlDriver "github.com/stephenafamo/bob/gen/bobgen-psql/driver"
17
        sqliteDriver "github.com/stephenafamo/bob/gen/bobgen-sqlite/driver"
18
        "github.com/stephenafamo/bob/gen/drivers"
19
        "github.com/stephenafamo/bob/gen/plugins"
20
        "github.com/testcontainers/testcontainers-go"
21
        mysqltest "github.com/testcontainers/testcontainers-go/modules/mysql"
22
        "github.com/testcontainers/testcontainers-go/modules/postgres"
23
)
24

25
type Config struct {
26
        helpers.Config `yaml:",squash"`
27

28
        // What dialect to generate with
29
        // psql | mysql | sqlite
30
        Dialect string
31
        // Glob pattern to match migration files
32
        Pattern string
33
        // The database schemas to generate models for
34
        Schemas []string
35
        // The name of this schema will not be included in the generated models
36
        // a context value can then be used to set the schema at runtime
37
        // useful for multi-tenant setups
38
        SharedSchema string `yaml:"shared_schema"`
39
        // How many tables to fetch in parallel
40
        Concurrency int
41
        // Which UUID package to use (gofrs or google)
42
        UUIDPkg string `yaml:"uuid_pkg"`
43
        fs      fs.FS
44
}
45

46
func RunPostgres(ctx context.Context, state *gen.State[any], config Config, pluginsConfig plugins.Config) error {
×
47
        d, err := getPsqlDriver(ctx, config)
×
48
        if err != nil {
×
49
                return fmt.Errorf("getting psql driver: %w", err)
×
50
        }
×
51

NEW
52
        plugins := plugins.All[any, any, psqlDriver.IndexExtra](pluginsConfig, gen.PSQLTemplates)
×
53

×
54
        return gen.Run(ctx, state, d, plugins...)
×
55
}
56

57
func getPsqlDriver(ctx context.Context, config Config) (psqlDriver.Interface, error) {
×
58
        postgresContainer, err := postgres.Run(
×
59
                ctx, "postgres:16",
×
60
                postgres.BasicWaitStrategies(),
×
61
                testcontainers.WithLogger(log.New(io.Discard, "", log.LstdFlags)),
×
62
        )
×
63
        if err != nil {
×
64
                return nil, fmt.Errorf("failed to start container: %w", err)
×
65
        }
×
66
        defer func() {
×
67
                if err := testcontainers.TerminateContainer(postgresContainer); err != nil {
×
68
                        log.Printf("failed to terminate container: %s", err)
×
69
                }
×
70
        }()
71

72
        dsn, err := postgresContainer.ConnectionString(ctx, "sslmode=disable")
×
73
        if err != nil {
×
74
                return nil, fmt.Errorf("failed to get connection string: %w", err)
×
75
        }
×
76

77
        db, err := sql.Open("postgres", dsn)
×
78
        if err != nil {
×
79
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
80
        }
×
81
        defer db.Close()
×
82

×
83
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
84
                return nil, fmt.Errorf("migrating: %w", err)
×
85
        }
×
86
        db.Close() // close early
×
87

×
88
        config.Dsn = dsn
×
89
        d := wrapDriver(ctx, psqlDriver.New(psqlDriver.Config{
×
90
                Config:       config.Config,
×
91
                Schemas:      pq.StringArray(config.Schemas),
×
92
                SharedSchema: config.SharedSchema,
×
93
                Concurrency:  config.Concurrency,
×
94
                UUIDPkg:      config.UUIDPkg,
×
95
        }))
×
96

×
97
        return d, nil
×
98
}
99

100
func RunMySQL(ctx context.Context, state *gen.State[any], config Config, pluginsConfig plugins.Config) error {
×
101
        d, err := getMySQLDriver(ctx, config)
×
102
        if err != nil {
×
103
                return fmt.Errorf("getting mysql driver: %w", err)
×
104
        }
×
105

NEW
106
        plugins := plugins.All[any, any, any](pluginsConfig, gen.MySQLTemplates)
×
107

×
108
        return gen.Run(ctx, state, d, plugins...)
×
109
}
110

111
func getMySQLDriver(ctx context.Context, config Config) (mysqlDriver.Interface, error) {
×
112
        mysqlContainer, err := mysqltest.Run(ctx,
×
113
                "mysql:8.0.35",
×
114
                mysqltest.WithDatabase("bobgen"),
×
115
                mysqltest.WithUsername("root"),
×
116
                mysqltest.WithPassword("password"),
×
117
        )
×
118
        defer func() {
×
119
                if err := testcontainers.TerminateContainer(mysqlContainer); err != nil {
×
120
                        fmt.Printf("failed to terminate MySQL container: %v\n", err)
×
121
                }
×
122
        }()
123
        if err != nil {
×
124
                return nil, fmt.Errorf("failed to start container: %w", err)
×
125
        }
×
126

127
        dsn, err := mysqlContainer.ConnectionString(ctx, "tls=skip-verify", "multiStatements=true", "parseTime=true")
×
128
        if err != nil {
×
129
                return nil, fmt.Errorf("failed to get connection string: %w", err)
×
130
        }
×
131

132
        db, err := sql.Open("mysql", dsn)
×
133
        if err != nil {
×
134
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
135
        }
×
136
        defer db.Close()
×
137

×
138
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
139
                return nil, fmt.Errorf("migrating: %w", err)
×
140
        }
×
141
        db.Close() // close early
×
142

×
143
        config.Dsn = dsn
×
144
        d := wrapDriver(ctx, mysqlDriver.New(mysqlDriver.Config{
×
145
                Config:      config.Config,
×
146
                Concurrency: config.Concurrency,
×
147
        }))
×
148

×
149
        return d, nil
×
150
}
151

152
func RunSQLite(ctx context.Context, state *gen.State[any], config Config, pluginsConfig plugins.Config) error {
×
153
        d, err := getSQLiteDriver(ctx, config)
×
154
        if err != nil {
×
155
                return fmt.Errorf("getting sqlite driver: %w", err)
×
156
        }
×
157

NEW
158
        plugins := plugins.All[any, any, sqliteDriver.IndexExtra](pluginsConfig, gen.SQLiteTemplates)
×
159

×
160
        return gen.Run(ctx, state, d, plugins...)
×
161
}
162

163
func getSQLiteDriver(ctx context.Context, config Config) (sqliteDriver.Interface, error) {
×
164
        tmp, err := os.CreateTemp("", "bobgen_sqlite")
×
165
        if err != nil {
×
166
                return nil, fmt.Errorf("creating temp file: %w", err)
×
167
        }
×
168
        defer tmp.Close()
×
169

×
170
        db, err := sql.Open("sqlite", tmp.Name())
×
171
        if err != nil {
×
172
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
173
        }
×
174
        defer db.Close()
×
175

×
176
        attach := make(map[string]string)
×
177
        for _, schema := range config.Schemas {
×
178
                tmp, err := os.CreateTemp("", "bobgen_sqlite_"+schema)
×
179
                if err != nil {
×
180
                        return nil, fmt.Errorf("creating temp file: %w", err)
×
181
                }
×
182
                defer tmp.Close()
×
183

×
184
                attach[schema] = tmp.Name()
×
185
                _, err = db.ExecContext(ctx, fmt.Sprintf(
×
186
                        "attach database '%s' as %s", tmp.Name(), schema,
×
187
                ))
×
188
                if err != nil {
×
189
                        return nil, fmt.Errorf("could not attach %q: %w", schema, err)
×
190
                }
×
191
        }
192

193
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
194
                return nil, fmt.Errorf("migrating: %w", err)
×
195
        }
×
196
        db.Close() // close early
×
197

×
198
        config.Dsn = "file:" + tmp.Name()
×
199
        d := sqliteDriver.New(sqliteDriver.Config{
×
200
                Config:       config.Config,
×
201
                Attach:       attach,
×
202
                SharedSchema: config.SharedSchema,
×
203
        })
×
204

×
205
        return d, nil
×
206
}
207

208
func wrapDriver[T, C, I any](ctx context.Context, d drivers.Interface[T, C, I]) driver[T, C, I] {
×
209
        info, err := d.Assemble(ctx)
×
210
        return driver[T, C, I]{d, info, err}
×
211
}
×
212

213
type driver[T, C, I any] struct {
214
        drivers.Interface[T, C, I]
215
        info *drivers.DBInfo[T, C, I]
216
        err  error
217
}
218

219
func (d driver[T, C, I]) Assemble(context.Context) (*drivers.DBInfo[T, C, I], error) {
×
220
        return d.info, d.err
×
221
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc