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

stephenafamo / bob / 15319368397

29 May 2025 08:08AM UTC coverage: 36.849% (-0.1%) from 36.981%
15319368397

push

github

stephenafamo
Import libsql driver in sqlite test

8035 of 21805 relevant lines covered (36.85%)

185.57 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/fs"
8
        "os"
9
        "path/filepath"
10

11
        embeddedpostgres "github.com/fergusstrange/embedded-postgres"
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/testcontainers/testcontainers-go"
20
        mysqltest "github.com/testcontainers/testcontainers-go/modules/mysql"
21
)
22

23
type Config struct {
24
        // What dialect to generate with
25
        // psql | mysql | sqlite
26
        Dialect string
27
        // Glob pattern to match migration files
28
        Pattern string
29
        // Folders containing query files
30
        Queries []string `yaml:"queries"`
31
        // The database schemas to generate models for
32
        Schemas []string
33
        // The name of this schema will not be included in the generated models
34
        // a context value can then be used to set the schema at runtime
35
        // useful for multi-tenant setups
36
        SharedSchema string `yaml:"shared_schema"`
37
        // List of tables that will be included. Others are ignored
38
        Only map[string][]string
39
        // List of tables that will be should be ignored. Others are included
40
        Except map[string][]string
41
        // How many tables to fetch in parallel
42
        Concurrency int
43
        // Which UUID package to use (gofrs or google)
44
        UUIDPkg string `yaml:"uuid_pkg"`
45
        // Which `database/sql` driver to use (the full module name)
46
        DriverName string `yaml:"driver_name"`
47

48
        Output    string
49
        Pkgname   string
50
        NoFactory bool `yaml:"no_factory"`
51

52
        fs fs.FS
53
}
54

55
func RunPostgres(ctx context.Context, state *gen.State[any], config Config) error {
×
56
        d, err := getPsqlDriver(ctx, config)
×
57
        if err != nil {
×
58
                return fmt.Errorf("getting psql driver: %w", err)
×
59
        }
×
60

61
        return gen.Run(ctx, state, d)
×
62
}
63

64
func getPsqlDriver(ctx context.Context, config Config) (psqlDriver.Interface, error) {
×
65
        port, err := helpers.GetFreePort()
×
66
        if err != nil {
×
67
                return nil, fmt.Errorf("could not get a free port: %w", err)
×
68
        }
×
69

70
        dbConfig := embeddedpostgres.
×
71
                DefaultConfig().
×
72
                RuntimePath(filepath.Join(os.TempDir(), "bobgen_sql")).
×
73
                Port(uint32(port))
×
74
        dsn := dbConfig.GetConnectionURL() + "?sslmode=disable"
×
75

×
76
        postgres := embeddedpostgres.NewDatabase(dbConfig)
×
77
        if err := postgres.Start(); err != nil {
×
78
                return nil, fmt.Errorf("starting embedded postgres: %w", err)
×
79
        }
×
80
        defer func() {
×
81
                if err := postgres.Stop(); err != nil {
×
82
                        fmt.Println("Error stopping postgres:", err)
×
83
                }
×
84
        }()
85

86
        db, err := sql.Open("postgres", dsn)
×
87
        if err != nil {
×
88
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
89
        }
×
90
        defer db.Close()
×
91

×
92
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
93
                return nil, fmt.Errorf("migrating: %w", err)
×
94
        }
×
95
        db.Close() // close early
×
96

×
97
        d := wrapDriver(ctx, psqlDriver.New(psqlDriver.Config{
×
98
                Config: helpers.Config{
×
99
                        Dsn:        dsn,
×
100
                        Queries:    config.Queries,
×
101
                        Only:       config.Only,
×
102
                        Except:     config.Except,
×
103
                        DriverName: config.DriverName,
×
104
                        Output:     config.Output,
×
105
                        Pkgname:    config.Pkgname,
×
106
                        NoFactory:  config.NoFactory,
×
107
                },
×
108
                Schemas:      pq.StringArray(config.Schemas),
×
109
                SharedSchema: config.SharedSchema,
×
110
                Concurrency:  config.Concurrency,
×
111
                UUIDPkg:      config.UUIDPkg,
×
112
        }))
×
113

×
114
        return d, nil
×
115
}
116

117
func RunMySQL(ctx context.Context, state *gen.State[any], config Config) error {
×
118
        d, err := getMySQLDriver(ctx, config)
×
119
        if err != nil {
×
120
                return fmt.Errorf("getting mysql driver: %w", err)
×
121
        }
×
122

123
        return gen.Run(ctx, state, d)
×
124
}
125

126
func getMySQLDriver(ctx context.Context, config Config) (mysqlDriver.Interface, error) {
×
127
        mysqlContainer, err := mysqltest.Run(ctx,
×
128
                "mysql:8.0.35",
×
129
                mysqltest.WithDatabase("bobgen"),
×
130
                mysqltest.WithUsername("root"),
×
131
                mysqltest.WithPassword("password"),
×
132
        )
×
133
        defer func() {
×
134
                if err := testcontainers.TerminateContainer(mysqlContainer); err != nil {
×
135
                        fmt.Printf("failed to terminate MySQL container: %v\n", err)
×
136
                }
×
137
        }()
138
        if err != nil {
×
139
                return nil, fmt.Errorf("failed to start container: %w", err)
×
140
        }
×
141

142
        dsn, err := mysqlContainer.ConnectionString(ctx, "tls=skip-verify", "multiStatements=true", "parseTime=true")
×
143
        if err != nil {
×
144
                return nil, fmt.Errorf("failed to get connection string: %w", err)
×
145
        }
×
146

147
        db, err := sql.Open("mysql", dsn)
×
148
        if err != nil {
×
149
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
150
        }
×
151
        defer db.Close()
×
152

×
153
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
154
                return nil, fmt.Errorf("migrating: %w", err)
×
155
        }
×
156
        db.Close() // close early
×
157

×
158
        d := wrapDriver(ctx, mysqlDriver.New(mysqlDriver.Config{
×
159
                Config: helpers.Config{
×
160
                        Dsn:       dsn,
×
161
                        Queries:   config.Queries,
×
162
                        Only:      config.Only,
×
163
                        Except:    config.Except,
×
164
                        Output:    config.Output,
×
165
                        Pkgname:   config.Pkgname,
×
166
                        NoFactory: config.NoFactory,
×
167
                },
×
168
                Concurrency: config.Concurrency,
×
169
        }))
×
170

×
171
        return d, nil
×
172
}
173

174
func RunSQLite(ctx context.Context, state *gen.State[any], config Config) error {
×
175
        d, err := getSQLiteDriver(ctx, config)
×
176
        if err != nil {
×
177
                return fmt.Errorf("getting sqlite driver: %w", err)
×
178
        }
×
179

180
        return gen.Run(ctx, state, d)
×
181
}
182

183
func getSQLiteDriver(ctx context.Context, config Config) (sqliteDriver.Interface, error) {
×
184
        tmp, err := os.CreateTemp("", "bobgen_sqlite")
×
185
        if err != nil {
×
186
                return nil, fmt.Errorf("creating temp file: %w", err)
×
187
        }
×
188
        defer tmp.Close()
×
189

×
190
        db, err := sql.Open("sqlite", tmp.Name())
×
191
        if err != nil {
×
192
                return nil, fmt.Errorf("failed to connect to database: %w", err)
×
193
        }
×
194
        defer db.Close()
×
195

×
196
        attach := make(map[string]string)
×
197
        for _, schema := range config.Schemas {
×
198
                tmp, err := os.CreateTemp("", "bobgen_sqlite_"+schema)
×
199
                if err != nil {
×
200
                        return nil, fmt.Errorf("creating temp file: %w", err)
×
201
                }
×
202
                defer tmp.Close()
×
203

×
204
                attach[schema] = tmp.Name()
×
205
                _, err = db.ExecContext(ctx, fmt.Sprintf(
×
206
                        "attach database '%s' as %s", tmp.Name(), schema,
×
207
                ))
×
208
                if err != nil {
×
209
                        return nil, fmt.Errorf("could not attach %q: %w", schema, err)
×
210
                }
×
211
        }
212

213
        if err := helpers.Migrate(ctx, db, config.fs, config.Pattern); err != nil {
×
214
                return nil, fmt.Errorf("migrating: %w", err)
×
215
        }
×
216
        db.Close() // close early
×
217

×
218
        d := sqliteDriver.New(sqliteDriver.Config{
×
219
                Config: helpers.Config{
×
220
                        Dsn:        tmp.Name(),
×
221
                        Queries:    config.Queries,
×
222
                        DriverName: config.DriverName,
×
223
                        Only:       config.Only,
×
224
                        Except:     config.Except,
×
225
                        Output:     config.Output,
×
226
                        Pkgname:    config.Pkgname,
×
227
                        NoFactory:  config.NoFactory,
×
228
                },
×
229
                Attach:       attach,
×
230
                SharedSchema: config.SharedSchema,
×
231
        })
×
232

×
233
        return d, nil
×
234
}
235

236
func wrapDriver[T, C, I any](ctx context.Context, d drivers.Interface[T, C, I]) driver[T, C, I] {
×
237
        info, err := d.Assemble(ctx)
×
238
        return driver[T, C, I]{d, info, err}
×
239
}
×
240

241
type driver[T, C, I any] struct {
242
        drivers.Interface[T, C, I]
243
        info *drivers.DBInfo[T, C, I]
244
        err  error
245
}
246

247
func (d driver[T, C, I]) Assemble(context.Context) (*drivers.DBInfo[T, C, I], error) {
×
248
        return d.info, d.err
×
249
}
×
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