Skip to content

Commit 4273f1c

Browse files
cpcloudclaude
andcommitted
test(data): add missing tests for audit fix coverage
Add unit tests for previously uncovered vendored sqlite functions: - ddlmod: TestRemoveColumn (quoted/unquoted/not-found variants), TestClone (deep copy independence), TestRenameTable (quoted/unquoted) - sqlite: TestOpen, TestDefaultValueOf, TestDataTypeOf (all branches), TestExplain, TestQuoteTo, TestSavePointAndRollbackTo, TestTranslateForeignKeyViolation Raises sqlite package coverage from 38.7% to 49.5%. Remaining uncovered code is migrator methods deeply coupled to GORM internals, exercised by the internal/data integration tests. Also adds -timeout 5m to CI test and benchmark steps to catch hangs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8d634ed commit 4273f1c

File tree

3 files changed

+261
-2
lines changed

3 files changed

+261
-2
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ jobs:
4848
run: echo "MICASA_TEST_SEED=${{ github.run_id }}"
4949

5050
- name: Test
51-
run: go test -race -shuffle on -v -coverprofile coverage.txt ./...
51+
run: go test -race -shuffle on -timeout 5m -v -coverprofile coverage.txt ./...
5252
env:
5353
CGO_ENABLED: "1"
5454
MICASA_TEST_SEED: ${{ github.run_id }}
5555

5656
- name: Benchmarks (smoke)
57-
run: go test -bench . -benchtime 1x -run '^$' ./...
57+
run: go test -bench . -benchtime 1x -timeout 5m -run '^$' ./...
5858

5959
- name: Coverage summary
6060
run: go tool cover -func coverage.txt

internal/data/sqlite/ddlmod_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,131 @@ func TestRemoveConstraint(t *testing.T) {
509509
}
510510
}
511511

512+
func TestRemoveColumn(t *testing.T) {
513+
tests := []struct {
514+
name string
515+
fields []string
516+
column string
517+
success bool
518+
expect []string
519+
}{
520+
{
521+
name: "backtick_quoted",
522+
fields: []string{"`id` integer NOT NULL", "`name` text"},
523+
column: "name",
524+
success: true,
525+
expect: []string{"`id` integer NOT NULL"},
526+
},
527+
{
528+
name: "double_quoted",
529+
fields: []string{`"id" integer NOT NULL`, `"name" text`},
530+
column: "name",
531+
success: true,
532+
expect: []string{`"id" integer NOT NULL`},
533+
},
534+
{
535+
name: "single_quoted",
536+
fields: []string{"'id' integer NOT NULL", "'name' text"},
537+
column: "name",
538+
success: true,
539+
expect: []string{"'id' integer NOT NULL"},
540+
},
541+
{
542+
name: "unquoted",
543+
fields: []string{"id integer NOT NULL", "name text"},
544+
column: "name",
545+
success: true,
546+
expect: []string{"id integer NOT NULL"},
547+
},
548+
{
549+
name: "not_found",
550+
fields: []string{"`id` integer NOT NULL", "`name` text"},
551+
column: "missing",
552+
success: false,
553+
expect: []string{"`id` integer NOT NULL", "`name` text"},
554+
},
555+
}
556+
557+
for _, tt := range tests {
558+
t.Run(tt.name, func(t *testing.T) {
559+
d := ddl{fields: tt.fields}
560+
ok := d.removeColumn(tt.column)
561+
assert.Equal(t, tt.success, ok)
562+
assert.Equal(t, tt.expect, d.fields)
563+
})
564+
}
565+
}
566+
567+
func TestClone(t *testing.T) {
568+
original := &ddl{
569+
head: "CREATE TABLE `test`",
570+
fields: []string{"`id` integer NOT NULL", "`name` text"},
571+
columns: []migrator.ColumnType{
572+
{NameValue: sql.NullString{String: "id", Valid: true}},
573+
},
574+
}
575+
576+
cloned := original.clone()
577+
578+
// Values should be equal
579+
assert.Equal(t, original.head, cloned.head)
580+
assert.Equal(t, original.fields, cloned.fields)
581+
assert.Equal(t, original.columns, cloned.columns)
582+
583+
// Mutating the clone should not affect the original
584+
cloned.fields[0] = "changed"
585+
assert.NotEqual(t, original.fields[0], cloned.fields[0])
586+
}
587+
588+
func TestRenameTable(t *testing.T) {
589+
tests := []struct {
590+
name string
591+
head string
592+
src string
593+
dst string
594+
wantErr bool
595+
}{
596+
{
597+
name: "backtick_quoted",
598+
head: "CREATE TABLE `users`",
599+
src: "users",
600+
dst: "users__temp",
601+
},
602+
{
603+
name: "double_quoted",
604+
head: `CREATE TABLE "users"`,
605+
src: "users",
606+
dst: "users__temp",
607+
},
608+
{
609+
name: "unquoted",
610+
head: "CREATE TABLE users",
611+
src: "users",
612+
dst: "users__temp",
613+
},
614+
{
615+
name: "not_found",
616+
head: "CREATE TABLE `something`",
617+
src: "other",
618+
dst: "other__temp",
619+
wantErr: true,
620+
},
621+
}
622+
623+
for _, tt := range tests {
624+
t.Run(tt.name, func(t *testing.T) {
625+
d := &ddl{head: tt.head}
626+
err := d.renameTable(tt.dst, tt.src)
627+
if tt.wantErr {
628+
assert.Error(t, err)
629+
} else {
630+
require.NoError(t, err)
631+
assert.Contains(t, d.head, tt.dst)
632+
}
633+
})
634+
}
635+
}
636+
512637
func TestGetColumns(t *testing.T) {
513638
params := []struct {
514639
name string

internal/data/sqlite/sqlite_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ package sqlite
1111
import (
1212
"database/sql"
1313
"fmt"
14+
"strings"
1415
"testing"
1516

1617
"github.com/stretchr/testify/assert"
1718
"github.com/stretchr/testify/require"
1819
"gorm.io/gorm"
20+
"gorm.io/gorm/clause"
1921
"gorm.io/gorm/logger"
22+
"gorm.io/gorm/schema"
2023

2124
modernsqlite "modernc.org/sqlite"
2225
)
@@ -126,6 +129,137 @@ func TestSQLiteVersion(t *testing.T) {
126129
t.Logf("SQLite version: %s", version)
127130
}
128131

132+
func TestOpen(t *testing.T) {
133+
d := Open(inMemoryDSN)
134+
assert.Equal(t, "sqlite", d.Name())
135+
136+
dialector, ok := d.(*Dialector)
137+
require.True(t, ok)
138+
assert.Equal(t, inMemoryDSN, dialector.DSN)
139+
assert.Empty(t, dialector.DriverName)
140+
}
141+
142+
func TestDefaultValueOf(t *testing.T) {
143+
d := Dialector{}
144+
145+
auto := d.DefaultValueOf(&schema.Field{AutoIncrement: true})
146+
assert.Equal(t, clause.Expr{SQL: "NULL"}, auto)
147+
148+
normal := d.DefaultValueOf(&schema.Field{AutoIncrement: false})
149+
assert.Equal(t, clause.Expr{SQL: "DEFAULT"}, normal)
150+
}
151+
152+
func TestDataTypeOf(t *testing.T) {
153+
d := Dialector{}
154+
155+
tests := []struct {
156+
field *schema.Field
157+
want string
158+
}{
159+
{&schema.Field{DataType: schema.Bool}, "numeric"},
160+
{&schema.Field{DataType: schema.Int}, "integer"},
161+
{
162+
&schema.Field{DataType: schema.Int, AutoIncrement: true},
163+
"integer PRIMARY KEY AUTOINCREMENT",
164+
},
165+
{&schema.Field{DataType: schema.Uint}, "integer"},
166+
{&schema.Field{DataType: schema.Float}, "real"},
167+
{&schema.Field{DataType: schema.String}, "text"},
168+
{&schema.Field{DataType: schema.Time}, "datetime"},
169+
{
170+
&schema.Field{DataType: schema.Time, TagSettings: map[string]string{"TYPE": "date"}},
171+
"date",
172+
},
173+
{&schema.Field{DataType: schema.Bytes}, "blob"},
174+
{&schema.Field{DataType: "custom_type"}, "custom_type"},
175+
}
176+
177+
for _, tt := range tests {
178+
t.Run(tt.want, func(t *testing.T) {
179+
assert.Equal(t, tt.want, d.DataTypeOf(tt.field))
180+
})
181+
}
182+
}
183+
184+
func TestExplain(t *testing.T) {
185+
d := Dialector{}
186+
result := d.Explain("SELECT * FROM users WHERE id = ?", 42)
187+
assert.Contains(t, result, "42")
188+
assert.Contains(t, result, "SELECT")
189+
}
190+
191+
func TestQuoteTo(t *testing.T) {
192+
d := Dialector{}
193+
194+
tests := []struct {
195+
input string
196+
want string
197+
}{
198+
{"simple", "`simple`"},
199+
{"schema.table", "`schema`.`table`"},
200+
}
201+
202+
for _, tt := range tests {
203+
t.Run(tt.input, func(t *testing.T) {
204+
var buf strings.Builder
205+
d.QuoteTo(&buf, tt.input)
206+
assert.Equal(t, tt.want, buf.String())
207+
})
208+
}
209+
}
210+
211+
func TestSavePointAndRollbackTo(t *testing.T) {
212+
db, err := gorm.Open(&Dialector{DSN: "file:test_savepoint?mode=memory"}, &gorm.Config{
213+
Logger: logger.Default.LogMode(logger.Silent),
214+
SkipDefaultTransaction: true,
215+
})
216+
require.NoError(t, err)
217+
218+
type Item struct {
219+
ID uint
220+
Name string
221+
}
222+
require.NoError(t, db.AutoMigrate(&Item{}))
223+
224+
d := Dialector{}
225+
226+
require.NoError(t, db.Create(&Item{Name: "before"}).Error)
227+
228+
require.NoError(t, d.SavePoint(db, "sp1"))
229+
require.NoError(t, db.Create(&Item{Name: "after_savepoint"}).Error)
230+
require.NoError(t, d.RollbackTo(db, "sp1"))
231+
232+
var count int64
233+
db.Model(&Item{}).Where("name = ?", "after_savepoint").Count(&count)
234+
assert.Equal(t, int64(0), count, "rollback should have undone the insert")
235+
}
236+
237+
func TestTranslateForeignKeyViolation(t *testing.T) {
238+
db, err := gorm.Open(&Dialector{DSN: "file:test_fk_violation?mode=memory"}, &gorm.Config{
239+
Logger: logger.Default.LogMode(logger.Silent),
240+
TranslateError: true,
241+
})
242+
require.NoError(t, err)
243+
244+
// Enable foreign keys
245+
require.NoError(t, db.Exec("PRAGMA foreign_keys = ON").Error)
246+
247+
type Parent struct {
248+
ID uint `gorm:"primarykey"`
249+
}
250+
type Child struct {
251+
ID uint `gorm:"primarykey"`
252+
ParentID uint
253+
Parent Parent `gorm:"foreignKey:ParentID"`
254+
}
255+
require.NoError(t, db.AutoMigrate(&Parent{}, &Child{}))
256+
257+
// Insert a child referencing a non-existent parent
258+
err = db.Create(&Child{ParentID: 9999}).Error
259+
require.Error(t, err)
260+
assert.ErrorIs(t, err, gorm.ErrForeignKeyViolated)
261+
}
262+
129263
func TestCompareVersion(t *testing.T) {
130264
tests := []struct {
131265
v1, v2 string

0 commit comments

Comments
 (0)