Skip to content

Commit 59c6911

Browse files
committed
wip: unit tests for upsert streamed
1 parent 585f3e5 commit 59c6911

File tree

2 files changed

+224
-8
lines changed

2 files changed

+224
-8
lines changed

database/upsert.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,24 @@ func UpsertStreamed[T any, V EntityConstraint[T]](
126126
entities <-chan T,
127127
options ...UpsertOption,
128128
) error {
129-
opts := &upsertOptions{}
129+
var (
130+
opts = &upsertOptions{}
131+
entityType = V(new(T))
132+
sem = db.GetSemaphoreForTable(TableName(entityType))
133+
stmt string
134+
placeholders int
135+
err error
136+
)
137+
130138
for _, option := range options {
131139
option.apply(opts)
132140
}
133141

134-
entityType := V(new(T))
135-
sem := db.GetSemaphoreForTable(TableName(entityType))
136-
137-
var stmt string
138-
var placeholders int
139-
140142
if opts.stmt != nil {
141-
stmt, placeholders, _ = BuildUpsertStatement(db, opts.stmt)
143+
stmt, placeholders, err = BuildUpsertStatement(db, opts.stmt)
144+
if err != nil {
145+
return err
146+
}
142147
} else {
143148
stmt, placeholders = db.BuildUpsertStmt(entityType)
144149
}

database/upsert_test.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"github.com/creasty/defaults"
6+
"github.com/icinga/icinga-go-library/logging"
7+
"github.com/icinga/icinga-go-library/testutils"
8+
"go.uber.org/zap/zapcore"
9+
"testing"
10+
"time"
11+
)
12+
13+
type UpsertStreamedTestData struct {
14+
Entities []MockEntity
15+
Statement UpsertStatement
16+
Callbacks []OnSuccess[MockEntity]
17+
}
18+
19+
func initTestDb(db *DB, logger *logging.Logger) {
20+
_, err := db.Query("DROP TABLE IF EXISTS mock_entity")
21+
if err != nil {
22+
logger.Fatal(err)
23+
}
24+
25+
_, err = db.Query(`CREATE TABLE mock_entity ("id" INTEGER PRIMARY KEY, "name" VARCHAR(255), "age" INTEGER, "email" VARCHAR(255))`)
26+
if err != nil {
27+
logger.Fatal(err)
28+
}
29+
30+
entities := []MockEntity{
31+
{Id: 1, Name: "test1", Age: 10, Email: "[email protected]"},
32+
{Id: 2, Name: "test2", Age: 20, Email: "[email protected]"},
33+
{Id: 3, Name: "test3", Age: 30, Email: "[email protected]"},
34+
{Id: 4, Name: "test4", Age: 40, Email: "[email protected]"},
35+
}
36+
37+
for _, entity := range entities {
38+
_, err = db.NamedExec(`INSERT INTO mock_entity ("id", "name", "age", "email") VALUES (:id, :name, :age, :email)`, entity)
39+
if err != nil {
40+
logger.Fatal(err)
41+
}
42+
}
43+
}
44+
45+
func TestUpsertStreamed(t *testing.T) {
46+
tests := []testutils.TestCase[[]MockEntity, UpsertStreamedTestData]{
47+
{
48+
Name: "Insert",
49+
Expected: []MockEntity{
50+
{Id: 1, Name: "test1", Age: 10, Email: "[email protected]"},
51+
{Id: 2, Name: "test2", Age: 20, Email: "[email protected]"},
52+
{Id: 3, Name: "test3", Age: 30, Email: "[email protected]"},
53+
{Id: 4, Name: "test4", Age: 40, Email: "[email protected]"},
54+
{Id: 5, Name: "test5", Age: 50, Email: "[email protected]"},
55+
{Id: 6, Name: "test6", Age: 60, Email: "[email protected]"},
56+
{Id: 7, Name: "test7", Age: 70, Email: "[email protected]"},
57+
{Id: 8, Name: "test8", Age: 80, Email: "[email protected]"},
58+
},
59+
Data: UpsertStreamedTestData{
60+
Entities: []MockEntity{
61+
{Id: 5, Name: "test5", Age: 50, Email: "[email protected]"},
62+
{Id: 6, Name: "test6", Age: 60, Email: "[email protected]"},
63+
{Id: 7, Name: "test7", Age: 70, Email: "[email protected]"},
64+
{Id: 8, Name: "test8", Age: 80, Email: "[email protected]"},
65+
},
66+
},
67+
},
68+
{
69+
Name: "Update",
70+
Expected: []MockEntity{
71+
{Id: 1, Name: "test1", Age: 100, Email: "[email protected]"},
72+
{Id: 2, Name: "test2", Age: 200, Email: "[email protected]"},
73+
{Id: 3, Name: "test3", Age: 300, Email: "[email protected]"},
74+
{Id: 4, Name: "test4", Age: 400, Email: "[email protected]"},
75+
},
76+
Data: UpsertStreamedTestData{
77+
Entities: []MockEntity{
78+
{Id: 1, Name: "test1", Age: 100, Email: "[email protected]"},
79+
{Id: 2, Name: "test2", Age: 200, Email: "[email protected]"},
80+
{Id: 3, Name: "test3", Age: 300, Email: "[email protected]"},
81+
{Id: 4, Name: "test4", Age: 400, Email: "[email protected]"},
82+
},
83+
},
84+
},
85+
{
86+
Name: "InsertAndUpdate",
87+
Expected: []MockEntity{
88+
{Id: 1, Name: "test1", Age: 10, Email: "[email protected]"},
89+
{Id: 2, Name: "test2", Age: 20, Email: "[email protected]"},
90+
{Id: 3, Name: "test3", Age: 30, Email: "[email protected]"},
91+
{Id: 4, Name: "test40", Age: 40, Email: "[email protected]"},
92+
{Id: 5, Name: "test50", Age: 50, Email: "[email protected]"},
93+
{Id: 6, Name: "test6", Age: 60, Email: "[email protected]"},
94+
},
95+
Data: UpsertStreamedTestData{
96+
Entities: []MockEntity{
97+
{Id: 5, Name: "test5", Age: 50, Email: "[email protected]"},
98+
{Id: 6, Name: "test6", Age: 60, Email: "[email protected]"},
99+
{Id: 4, Name: "test40", Age: 40, Email: "[email protected]"},
100+
{Id: 5, Name: "test50", Age: 50, Email: "[email protected]"},
101+
},
102+
},
103+
},
104+
{
105+
Name: "WithStatement",
106+
Expected: []MockEntity{
107+
{Id: 1, Name: "test1", Age: 10, Email: "[email protected]"},
108+
{Id: 2, Name: "test2", Age: 20, Email: "[email protected]"},
109+
{Id: 3, Name: "test3", Age: 30, Email: "[email protected]"},
110+
{Id: 4, Name: "test4", Age: 40, Email: "[email protected]"},
111+
{Id: 5, Name: "test5", Age: 50, Email: "[email protected]"},
112+
},
113+
Data: UpsertStreamedTestData{
114+
Entities: []MockEntity{
115+
{Id: 5, Name: "test5", Age: 50, Email: "[email protected]"},
116+
},
117+
Statement: NewUpsertStatement(&MockEntity{}),
118+
},
119+
},
120+
{
121+
Name: "WithFalseStatement",
122+
Error: testutils.ErrorContains("can't perform"), // TODO (jr): is it the right way?
123+
Data: UpsertStreamedTestData{
124+
Entities: []MockEntity{
125+
{Id: 5, Name: "test5", Age: 50, Email: "[email protected]"},
126+
},
127+
Statement: NewUpsertStatement(&MockEntity{}).Into("false_table"),
128+
},
129+
},
130+
}
131+
132+
var (
133+
upsertError error
134+
defaultOptions Options
135+
ctx = context.Background()
136+
entities = make(chan MockEntity)
137+
)
138+
139+
logs, err := logging.NewLoggingFromConfig(
140+
"Icinga Kubernetes",
141+
logging.Config{Level: zapcore.DebugLevel, Output: "console", Interval: time.Second},
142+
)
143+
if err != nil {
144+
t.Fatalf("cannot configure logging: %v", err)
145+
}
146+
147+
err = defaults.Set(&defaultOptions)
148+
if err != nil {
149+
t.Fatalf("cannot set default options: %v", err)
150+
}
151+
152+
db, err := NewDbFromConfig(
153+
&Config{Type: "sqlite", Database: ":memory:test-upsert-streamed", Options: defaultOptions},
154+
logs.GetChildLogger("database"),
155+
RetryConnectorCallbacks{},
156+
)
157+
if err != nil {
158+
t.Fatalf("cannot configure database: %v", err)
159+
}
160+
161+
for _, tst := range tests {
162+
t.Run(tst.Name, tst.F(func(data UpsertStreamedTestData) ([]MockEntity, error) {
163+
ctx, cancel := context.WithCancel(ctx)
164+
165+
go func() {
166+
if tst.Data.Statement != nil {
167+
upsertError = UpsertStreamed(ctx, db, entities, tst.Data.Statement)
168+
} else {
169+
upsertError = UpsertStreamed(ctx, db, entities)
170+
}
171+
}()
172+
173+
initTestDb(db, logs.GetChildLogger("initTestDb"))
174+
175+
for _, entity := range tst.Data.Entities {
176+
entities <- entity
177+
}
178+
179+
var actual []MockEntity
180+
181+
time.Sleep(time.Second)
182+
183+
err = db.Select(&actual, "SELECT * FROM mock_entity")
184+
if err != nil {
185+
t.Fatalf("cannot select from database: %v", err)
186+
}
187+
188+
cancel()
189+
190+
return actual, upsertError
191+
}))
192+
193+
}
194+
195+
_ = db.Close()
196+
}
197+
198+
//func TestUpsertStreamedCallback(t *testing.T) {
199+
// tests := []testutils.TestCase[any, UpsertStreamedTestData]{
200+
// {
201+
// Name: "OneCallback",
202+
// Data: UpsertStreamedTestData{
203+
// Callbacks: []OnSuccess[MockEntity]{
204+
// func(ctx context.Context, affectedRows []MockEntity) error {
205+
//
206+
// },
207+
// },
208+
// },
209+
// },
210+
// }
211+
//}

0 commit comments

Comments
 (0)