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

stephenafamo / bob / 15260384356

26 May 2025 06:54PM UTC coverage: 36.966% (+6.1%) from 30.915%
15260384356

Pull #429

github

stephenafamo
Remove redundant testing in `bobgen-sql`
Pull Request #429: Add test for sql query gen in `bobgen-psql`

73 of 77 new or added lines in 7 files covered. (94.81%)

114 existing lines in 1 file now uncovered.

8018 of 21690 relevant lines covered (36.97%)

117.96 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

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

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

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

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

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

×
UNCOV
97
        d := wrapDriver(ctx, psqlDriver.New(psqlDriver.Config{
×
UNCOV
98
                Dsn: dsn,
×
UNCOV
99

×
UNCOV
100
                Schemas:      pq.StringArray(config.Schemas),
×
UNCOV
101
                SharedSchema: config.SharedSchema,
×
UNCOV
102
                Queries:      config.Queries,
×
UNCOV
103
                Only:         config.Only,
×
UNCOV
104
                Except:       config.Except,
×
UNCOV
105
                Concurrency:  config.Concurrency,
×
UNCOV
106
                UUIDPkg:      config.UUIDPkg,
×
UNCOV
107
                DriverName:   config.DriverName,
×
UNCOV
108
                Output:       config.Output,
×
UNCOV
109
                Pkgname:      config.Pkgname,
×
UNCOV
110
                NoFactory:    config.NoFactory,
×
UNCOV
111
        }))
×
UNCOV
112

×
UNCOV
113
        return d, nil
×
114
}
115

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

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

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

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

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

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

×
UNCOV
157
        d := wrapDriver(ctx, mysqlDriver.New(mysqlDriver.Config{
×
UNCOV
158
                Dsn: dsn,
×
UNCOV
159

×
UNCOV
160
                Queries:     config.Queries,
×
UNCOV
161
                Only:        config.Only,
×
UNCOV
162
                Except:      config.Except,
×
UNCOV
163
                Concurrency: config.Concurrency,
×
UNCOV
164
                Output:      config.Output,
×
UNCOV
165
                Pkgname:     config.Pkgname,
×
UNCOV
166
                NoFactory:   config.NoFactory,
×
UNCOV
167
        }))
×
UNCOV
168

×
UNCOV
169
        return d, nil
×
170
}
171

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

178
        return gen.Run(ctx, state, d)
×
179
}
180

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

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

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

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

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

×
UNCOV
216
        d := sqliteDriver.New(sqliteDriver.Config{
×
UNCOV
217
                DSN:        tmp.Name(),
×
UNCOV
218
                Attach:     attach,
×
UNCOV
219
                Queries:    config.Queries,
×
UNCOV
220
                DriverName: config.DriverName,
×
UNCOV
221

×
UNCOV
222
                SharedSchema: config.SharedSchema,
×
UNCOV
223
                Only:         config.Only,
×
UNCOV
224
                Except:       config.Except,
×
UNCOV
225
                Output:       config.Output,
×
UNCOV
226
                Pkgname:      config.Pkgname,
×
UNCOV
227
                NoFactory:    config.NoFactory,
×
UNCOV
228
        })
×
UNCOV
229

×
UNCOV
230
        return d, nil
×
231
}
232

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

238
type driver[T, C, I any] struct {
239
        drivers.Interface[T, C, I]
240
        info *drivers.DBInfo[T, C, I]
241
        err  error
242
}
243

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