From 12936f2c203fe402750f1a2bafb925c7263542f6 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Thu, 20 Feb 2025 21:13:30 +1000 Subject: [PATCH] more repository tests --- internal/chunk/dbproc/repository/dbchunk.go | 1 + .../chunk/dbproc/repository/dbchunk_test.go | 19 ++- internal/chunk/dbproc/repository/dbfile.go | 2 +- .../chunk/dbproc/repository/dbfile_test.go | 69 ++++++++ .../chunk/dbproc/repository/dbmessage_test.go | 74 ++++++++- .../dbproc/repository/dbsearch_file_test.go | 45 ++++++ .../chunk/dbproc/repository/dbsearch_msg.go | 1 - .../dbproc/repository/dbsearch_msg_test.go | 67 ++++++++ .../chunk/dbproc/repository/dbuser_test.go | 72 +++++++++ .../dbproc/repository/dbworkspace_test.go | 82 +++++++--- internal/chunk/dbproc/repository/generic.go | 9 +- .../chunk/dbproc/repository/generic_test.go | 151 +++++++++++++----- .../chunk/dbproc/repository/repository.go | 2 +- .../dbproc/repository/repository_test.go | 2 +- 14 files changed, 524 insertions(+), 72 deletions(-) create mode 100644 internal/chunk/dbproc/repository/dbfile_test.go create mode 100644 internal/chunk/dbproc/repository/dbsearch_file_test.go create mode 100644 internal/chunk/dbproc/repository/dbsearch_msg_test.go create mode 100644 internal/chunk/dbproc/repository/dbuser_test.go diff --git a/internal/chunk/dbproc/repository/dbchunk.go b/internal/chunk/dbproc/repository/dbchunk.go index 70811535..ada6441d 100644 --- a/internal/chunk/dbproc/repository/dbchunk.go +++ b/internal/chunk/dbproc/repository/dbchunk.go @@ -54,6 +54,7 @@ func (d DBChunk) values() []any { } type ChunkRepository interface { + // Insert should insert dbchunk into the repository and return its ID. Insert(ctx context.Context, conn sqlx.ExtContext, dbchunk *DBChunk) (int64, error) } diff --git a/internal/chunk/dbproc/repository/dbchunk_test.go b/internal/chunk/dbproc/repository/dbchunk_test.go index 58e72787..6b418387 100644 --- a/internal/chunk/dbproc/repository/dbchunk_test.go +++ b/internal/chunk/dbproc/repository/dbchunk_test.go @@ -7,8 +7,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/rusq/slackdump/v3/internal/chunk" "github.com/stretchr/testify/assert" + + "github.com/rusq/slackdump/v3/internal/chunk" ) func Test_chunkRepository_Insert(t *testing.T) { @@ -47,6 +48,22 @@ func Test_chunkRepository_Insert(t *testing.T) { want: 1, wantErr: assert.NoError, }, + { + name: "missing session", + args: args{ + ctx: context.Background(), + conn: testConn(t), + chunk: &DBChunk{ + SessionID: 1, + UnixTS: 1234567890, + TypeID: chunk.CMessages, + NumRecords: 100, + Final: true, + }, + }, + want: 0, + wantErr: assert.Error, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/chunk/dbproc/repository/dbfile.go b/internal/chunk/dbproc/repository/dbfile.go index 71735964..d613286a 100644 --- a/internal/chunk/dbproc/repository/dbfile.go +++ b/internal/chunk/dbproc/repository/dbfile.go @@ -49,7 +49,7 @@ func NewDBFile(chunkID int64, idx int, channelID, threadTS string, parentMsgTS s Index: idx, Mode: file.Mode, Filename: orNull(file.Name != "", file.Name), - URL: orNull(file.URLPrivate != "", file.URLPrivate), + URL: orNull(file.URLPrivateDownload != "", file.URLPrivateDownload), Data: data, }, nil } diff --git a/internal/chunk/dbproc/repository/dbfile_test.go b/internal/chunk/dbproc/repository/dbfile_test.go new file mode 100644 index 00000000..02c6ddb7 --- /dev/null +++ b/internal/chunk/dbproc/repository/dbfile_test.go @@ -0,0 +1,69 @@ +package repository + +import ( + "testing" + "time" + + "github.com/rusq/slack" + "github.com/stretchr/testify/assert" +) + +var ( + file1 = &slack.File{ + ID: "FILE1", + Created: 0, + Timestamp: slack.JSONTime(time.Date(1984, 9, 16, 15, 0, 0, 0, time.UTC).Unix()), + Name: "SOKO.COM", + Title: "Classic Sokoban Game, (c) 1984 Spectrum Holobyte", + URLPrivateDownload: "https://archive.org/details/msdos_sokoban_1984_spectrum_holobyte", + NumStars: 555, + Mode: "hosted", + } + + dbFile1, _ = NewDBFile(1, 0, "C1", "1631820000.000000", "1531820000.000000", file1) +) + +func TestNewDBFile(t *testing.T) { + type args struct { + chunkID int64 + idx int + channelID string + threadTS string + parentMsgTS string + file *slack.File + } + tests := []struct { + name string + args args + want *DBFile + wantErr bool + }{ + { + "success", + args{1, 42, "C1", "1631820000.000000", "1531820000.000000", file1}, + &DBFile{ + ID: "FILE1", + ChunkID: 1, + ChannelID: "C1", + MessageID: ptr[int64](1531820000000000), + ThreadID: ptr[int64](1631820000000000), + Index: 42, + Mode: "hosted", + Filename: ptr("SOKO.COM"), + URL: ptr("https://archive.org/details/msdos_sokoban_1984_spectrum_holobyte"), + Data: must(marshal(file1)), + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewDBFile(tt.args.chunkID, tt.args.idx, tt.args.channelID, tt.args.threadTS, tt.args.parentMsgTS, tt.args.file) + if (err != nil) != tt.wantErr { + t.Errorf("NewDBFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/chunk/dbproc/repository/dbmessage_test.go b/internal/chunk/dbproc/repository/dbmessage_test.go index e5371bed..4e1c946d 100644 --- a/internal/chunk/dbproc/repository/dbmessage_test.go +++ b/internal/chunk/dbproc/repository/dbmessage_test.go @@ -4,14 +4,14 @@ import ( "context" "encoding/json" "iter" + "reflect" "testing" "github.com/jmoiron/sqlx" "github.com/rusq/slack" - "github.com/stretchr/testify/assert" - "github.com/rusq/slackdump/v3/internal/chunk" "github.com/rusq/slackdump/v3/internal/fixtures" + "github.com/stretchr/testify/assert" ) func minifyJSON[T any](t *testing.T, s string) []byte { @@ -392,7 +392,21 @@ func Test_messageRepository_CountThread(t *testing.T) { want int64 wantErr bool }{ - // TODO: Add test cases. + { + name: "ok", + fields: fields{ + genericRepository: genericRepository[DBMessage]{DBMessage{}}, + }, + args: args{ + ctx: context.Background(), + conn: testConn(t), + channelID: "C123", + threadID: "123.456", + }, + prepFn: threadSetupFn, + want: 4, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -515,3 +529,57 @@ func Test_messageRepository_AllForThread(t *testing.T) { }) } } + +func TestDBMessage_Val(t *testing.T) { + type fields struct { + ID int64 + ChunkID int64 + ChannelID string + TS string + ParentID *int64 + ThreadTS *string + IsParent bool + Index int + NumFiles int + Text string + Data []byte + } + tests := []struct { + name string + fields fields + want slack.Message + wantErr bool + }{ + { + "ok", + fields(*dbmA), + msgA, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dbm := DBMessage{ + ID: tt.fields.ID, + ChunkID: tt.fields.ChunkID, + ChannelID: tt.fields.ChannelID, + TS: tt.fields.TS, + ParentID: tt.fields.ParentID, + ThreadTS: tt.fields.ThreadTS, + IsParent: tt.fields.IsParent, + Index: tt.fields.Index, + NumFiles: tt.fields.NumFiles, + Text: tt.fields.Text, + Data: tt.fields.Data, + } + got, err := dbm.Val() + if (err != nil) != tt.wantErr { + t.Errorf("DBMessage.Val() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("DBMessage.Val() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/chunk/dbproc/repository/dbsearch_file_test.go b/internal/chunk/dbproc/repository/dbsearch_file_test.go new file mode 100644 index 00000000..544fd719 --- /dev/null +++ b/internal/chunk/dbproc/repository/dbsearch_file_test.go @@ -0,0 +1,45 @@ +package repository + +import ( + "testing" + + "github.com/rusq/slack" + "github.com/stretchr/testify/assert" +) + +func TestNewDBSearchFile(t *testing.T) { + type args struct { + chunkID int64 + n int + sf *slack.File + } + tests := []struct { + name string + args args + want *DBSearchFile + wantErr bool + }{ + { + name: "creates a new DBSearchFile", + args: args{chunkID: 42, n: 50, sf: file1}, + want: &DBSearchFile{ + ID: 0, // autoincrement, handled by the database. + ChunkID: 42, + FileID: "FILE1", + Index: 50, + Data: must(marshal(file1)), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewDBSearchFile(tt.args.chunkID, tt.args.n, tt.args.sf) + if (err != nil) != tt.wantErr { + t.Errorf("NewDBSearchFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/chunk/dbproc/repository/dbsearch_msg.go b/internal/chunk/dbproc/repository/dbsearch_msg.go index 23d46178..607d2f98 100644 --- a/internal/chunk/dbproc/repository/dbsearch_msg.go +++ b/internal/chunk/dbproc/repository/dbsearch_msg.go @@ -9,7 +9,6 @@ import ( type DBSearchMessage struct { ID int64 `db:"ID"` ChunkID int64 `db:"CHUNK_ID"` - LoadDTTM string `db:"LOAD_DTTM,omitempty"` ChannelID string `db:"CHANNEL_ID"` ChannelName *string `db:"CHANNEL_NAME,omitempty"` TS string `db:"TS"` diff --git a/internal/chunk/dbproc/repository/dbsearch_msg_test.go b/internal/chunk/dbproc/repository/dbsearch_msg_test.go new file mode 100644 index 00000000..293c1dc1 --- /dev/null +++ b/internal/chunk/dbproc/repository/dbsearch_msg_test.go @@ -0,0 +1,67 @@ +package repository + +import ( + "reflect" + "testing" + + "github.com/rusq/slack" +) + +var srchMsg1 = &slack.SearchMessage{ + Type: "message", + Channel: slack.CtxChannel{ + ID: "C123", + Name: "chur", + }, + User: "U123", + Username: "bob", + Timestamp: "1725318212.603879", + Text: "Hello, world!", + Permalink: "http://slackdump.slack.com/archives/C123/p1725318212603879", +} + +func TestNewDBSearchMessage(t *testing.T) { + type args struct { + chunkID int64 + idx int + sm *slack.SearchMessage + } + tests := []struct { + name string + args args + want *DBSearchMessage + wantErr bool + }{ + { + name: "creates a new DBSearchMessage", + args: args{ + chunkID: 42, + idx: 50, + sm: srchMsg1, + }, + want: &DBSearchMessage{ + ID: 0, // autoincrement, handled by the database. + ChunkID: 42, + ChannelID: "C123", + ChannelName: ptr("chur"), + TS: "1725318212.603879", + Text: ptr("Hello, world!"), + IDX: 50, + Data: must(marshal(srchMsg1)), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewDBSearchMessage(tt.args.chunkID, tt.args.idx, tt.args.sm) + if (err != nil) != tt.wantErr { + t.Errorf("NewDBSearchMessage() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewDBSearchMessage() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/chunk/dbproc/repository/dbuser_test.go b/internal/chunk/dbproc/repository/dbuser_test.go new file mode 100644 index 00000000..eaac0911 --- /dev/null +++ b/internal/chunk/dbproc/repository/dbuser_test.go @@ -0,0 +1,72 @@ +package repository + +import ( + "reflect" + "testing" + + "github.com/rusq/slack" +) + +var user1 = &slack.User{ + ID: "U123", + TeamID: "T777", + Name: "bob", + Deleted: false, + Color: "#ff0000", // roses are red, violets are red, everything is red, hungry, poor and sad + RealName: "Dominic Decocco", + TZ: "Pacific/Auckland", + TZLabel: "NZDT", + TZOffset: 46800, + Profile: slack.UserProfile{ + FirstName: "Dominic", + LastName: "Decocco", + RealName: "", + RealNameNormalized: "", + DisplayName: "dom", + DisplayNameNormalized: "", + Team: "T777", + }, + Has2FA: true, + TwoFactorType: new(string), + Updated: 1725318212, + Enterprise: slack.EnterpriseUser{}, +} + +func TestNewDBUser(t *testing.T) { + type args struct { + chunkID int64 + n int + u *slack.User + } + tests := []struct { + name string + args args + want *DBUser + wantErr bool + }{ + { + name: "creates a new DBUser", + args: args{chunkID: 42, n: 50, u: user1}, + want: &DBUser{ + ID: "U123", + ChunkID: 42, + Username: "bob", + Index: 50, + Data: must(marshal(user1)), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewDBUser(tt.args.chunkID, tt.args.n, tt.args.u) + if (err != nil) != tt.wantErr { + t.Errorf("NewDBUser() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewDBUser() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/chunk/dbproc/repository/dbworkspace_test.go b/internal/chunk/dbproc/repository/dbworkspace_test.go index 29c62f8d..7f8ea746 100644 --- a/internal/chunk/dbproc/repository/dbworkspace_test.go +++ b/internal/chunk/dbproc/repository/dbworkspace_test.go @@ -5,29 +5,32 @@ import ( "testing" "github.com/jmoiron/sqlx" - "github.com/stretchr/testify/assert" - "github.com/rusq/slack" + "github.com/stretchr/testify/assert" "github.com/rusq/slackdump/v3/internal/chunk" ) +var wsp1 = &slack.AuthTestResponse{ + URL: "http://lol.slack.com", + Team: "lolzteam", + User: "lolzuser", + TeamID: "T123456", + UserID: "U123456", +} + +var wsp1_ = &slack.AuthTestResponse{ + URL: wsp1.URL, + Team: wsp1.Team, + User: "renameduser", + TeamID: wsp1.TeamID, + UserID: wsp1.UserID, +} + func Test_workspaceRepository_GetWorkspace(t *testing.T) { var ( - wsp1, _ = NewDBWorkspace(1, &slack.AuthTestResponse{ - URL: "http://lol.slack.com", - Team: "lolzteam", - User: "lolzuser", - TeamID: "T123456", - UserID: "U123456", - }) - wsp2, _ = NewDBWorkspace(2, &slack.AuthTestResponse{ - URL: wsp1.URL, - Team: wsp1.Team, - User: "renameduser", - TeamID: wsp1.TeamID, - UserID: wsp1.UserID, - }) + dbwsp1, _ = NewDBWorkspace(1, wsp1) + dbwsp2, _ = NewDBWorkspace(2, wsp1_) ) type fields struct { genericRepository genericRepository[DBWorkspace] @@ -57,11 +60,11 @@ func Test_workspaceRepository_GetWorkspace(t *testing.T) { t.Helper() prepChunk(chunk.CWorkspaceInfo, chunk.CWorkspaceInfo)(t, conn) wr := NewWorkspaceRepository() - if err := wr.Insert(context.Background(), conn, wsp1, wsp2); err != nil { + if err := wr.Insert(context.Background(), conn, dbwsp1, dbwsp2); err != nil { t.Fatal(err) } }, - want: *wsp2, + want: *dbwsp2, }, } for _, tt := range tests { @@ -81,3 +84,46 @@ func Test_workspaceRepository_GetWorkspace(t *testing.T) { }) } } + +func TestNewDBWorkspace(t *testing.T) { + type args struct { + chunkID int64 + wi *slack.AuthTestResponse + } + tests := []struct { + name string + args args + want *DBWorkspace + wantErr bool + }{ + { + name: "creates a new DBWorkspace", + args: args{ + chunkID: 42, + wi: wsp1, + }, + want: &DBWorkspace{ + ID: 0, + ChunkID: 42, + Team: wsp1.Team, + User: ptr(wsp1.User), + TeamID: "T123456", + UserID: "U123456", + EnterpriseID: nil, + URL: "http://lol.slack.com", + Data: must(marshal(wsp1)), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewDBWorkspace(tt.args.chunkID, tt.args.wi) + if (err != nil) != tt.wantErr { + t.Errorf("NewDBWorkspace() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/internal/chunk/dbproc/repository/generic.go b/internal/chunk/dbproc/repository/generic.go index 0014d5cf..c9bfc7a4 100644 --- a/internal/chunk/dbproc/repository/generic.go +++ b/internal/chunk/dbproc/repository/generic.go @@ -76,13 +76,6 @@ func (r genericRepository[T]) stmtInsert() string { const CTypeAny = chunk.CAny -// stmtLatest returns the statement that selects the latest chunk for each -// entity. it is only suitable for dictionary type entries, such as channels or -// users. -func (r genericRepository[T]) stmtLatest(tid chunk.ChunkType) (stmt string, binds []any) { - return r.stmtLatestWhere(tid, queryParams{}) -} - func colAlias(alias string, col ...string) string { var buf strings.Builder var prefix string @@ -94,6 +87,8 @@ func colAlias(alias string, col ...string) string { return buf.String() } +// stmtLatestWhere returns the statement that selects the latest chunk for +// entity. func (r genericRepository[T]) stmtLatestWhere(tid chunk.ChunkType, qp queryParams) (string, []any) { const alias = "T" var buf strings.Builder diff --git a/internal/chunk/dbproc/repository/generic_test.go b/internal/chunk/dbproc/repository/generic_test.go index 5c57da71..db10d749 100644 --- a/internal/chunk/dbproc/repository/generic_test.go +++ b/internal/chunk/dbproc/repository/generic_test.go @@ -15,42 +15,6 @@ import ( "github.com/rusq/slackdump/v3/internal/fixtures" ) -func Test_genericRepository_stmtLatest(t *testing.T) { - type args struct { - tid chunk.ChunkType - } - type testCase[T dbObject] struct { - name string - r genericRepository[T] - args args - wantStmt string - wantBinds []any - } - tests := []testCase[*DBChannel]{ - { - name: "generates for all channels", - r: genericRepository[*DBChannel]{t: new(DBChannel)}, - args: args{tid: CTypeAny}, - wantStmt: `SELECT T.ID, MAX(CHUNK_ID) AS CHUNK_ID FROM CHANNEL AS T JOIN CHUNK AS CH ON CH.ID = T.CHUNK_ID WHERE 1=1 GROUP BY T.ID`, - wantBinds: nil, - }, - { - name: "generates for concrete type", - r: genericRepository[*DBChannel]{t: new(DBChannel)}, - args: args{tid: chunk.CChannelInfo}, - wantStmt: `SELECT T.ID, MAX(CHUNK_ID) AS CHUNK_ID FROM CHANNEL AS T JOIN CHUNK AS CH ON CH.ID = T.CHUNK_ID WHERE 1=1 AND CH.TYPE_ID = ? GROUP BY T.ID`, - wantBinds: []any{chunk.CChannelInfo}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotStmt, gotBinds := tt.r.stmtLatest(tt.args.tid) - assert.Equalf(t, tt.wantStmt, gotStmt, "stmtLatest(%v)", tt.args.tid) - assert.Equalf(t, tt.wantBinds, gotBinds, "stmtLatest(%v)", tt.args.tid) - }) - } -} - func must[T any](t T, err error) T { if err != nil { panic(err) @@ -58,7 +22,7 @@ func must[T any](t T, err error) T { return t } -func Test_genericRepository_AllOfType(t *testing.T) { +func Test_genericRepository_allOfTypeWhere(t *testing.T) { allTestChans := fixtures.Load[[]slack.Channel](fixtures.TestChannels) data1 := must(marshal(allTestChans[0])) data2 := must(marshal(allTestChans[1])) @@ -67,6 +31,7 @@ func Test_genericRepository_AllOfType(t *testing.T) { ctx context.Context conn sqlx.QueryerContext typeID chunk.ChunkType + qp queryParams } type testCase[T dbObject] struct { name string @@ -125,14 +90,75 @@ func Test_genericRepository_AllOfType(t *testing.T) { }, wantErr: assert.NoError, }, + { + name: "Additional conditions in the query parameters", + r: genericRepository[DBChannel]{DBChannel{}}, + args: args{ + ctx: context.Background(), + conn: testConn(t), + typeID: chunk.CChannelInfo, + qp: queryParams{ + Where: "T.ID IN (?, ?)", + Binds: []any{"ABC", "CDE"}, + OrderBy: []string{"T.NAME DESC"}, // NOTE: descending. + }, + }, + prepFn: func(t *testing.T, conn PrepareExtContext) { + prepChunk(chunk.CChannelInfo, chunk.CChannelInfo, chunk.CChannelInfo)(t, conn) + cir := NewChannelRepository() + _, err := cir.InsertAll(context.Background(), conn, toIter([]testResult[*DBChannel]{ + {V: &DBChannel{ID: "BCD", ChunkID: 1, Name: ptr("other name"), Data: data2}, Err: nil}, + {V: &DBChannel{ID: "ABC", ChunkID: 1, Name: ptr("old name"), Data: data1}, Err: nil}, + {V: &DBChannel{ID: "ABC", ChunkID: 2, Name: ptr("new name"), Data: data1}, Err: nil}, + {V: &DBChannel{ID: "CDE", ChunkID: 2, Name: ptr("cde channel"), Data: data1}, Err: nil}, + })) + require.NoError(t, err) + }, + want: []testResult[DBChannel]{ + {V: DBChannel{ID: "ABC", ChunkID: 2, Name: ptr("new name"), Data: data1}, Err: nil}, + {V: DBChannel{ID: "CDE", ChunkID: 2, Name: ptr("cde channel"), Data: data1}, Err: nil}, + }, + wantErr: assert.NoError, + }, + { + name: "Same, but user key ordering (ID)", + r: genericRepository[DBChannel]{DBChannel{}}, + args: args{ + ctx: context.Background(), + conn: testConn(t), + typeID: chunk.CChannelInfo, + qp: queryParams{ + Where: "T.ID IN (?, ?)", + Binds: []any{"ABC", "CDE"}, + UserKeyOrder: true, + }, + }, + prepFn: func(t *testing.T, conn PrepareExtContext) { + prepChunk(chunk.CChannelInfo, chunk.CChannelInfo, chunk.CChannelInfo)(t, conn) + cir := NewChannelRepository() + _, err := cir.InsertAll(context.Background(), conn, toIter([]testResult[*DBChannel]{ + {V: &DBChannel{ID: "BCD", ChunkID: 1, Name: ptr("other name"), Data: data2}, Err: nil}, + {V: &DBChannel{ID: "ABC", ChunkID: 1, Name: ptr("old name"), Data: data1}, Err: nil}, + {V: &DBChannel{ID: "ABC", ChunkID: 2, Name: ptr("new name"), Data: data1}, Err: nil}, + {V: &DBChannel{ID: "CDE", ChunkID: 2, Name: ptr("cde channel"), Data: data1}, Err: nil}, + })) + require.NoError(t, err) + }, + want: []testResult[DBChannel]{ + // user key is ID. + {V: DBChannel{ID: "ABC", ChunkID: 2, Name: ptr("new name"), Data: data1}, Err: nil}, + {V: DBChannel{ID: "CDE", ChunkID: 2, Name: ptr("cde channel"), Data: data1}, Err: nil}, + }, + wantErr: assert.NoError, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.prepFn != nil { tt.prepFn(t, tt.args.conn.(PrepareExtContext)) } - got, err := tt.r.AllOfType(tt.args.ctx, tt.args.conn, tt.args.typeID) - if !tt.wantErr(t, err, fmt.Sprintf("AllOfType(%v, %v, %v)", tt.args.ctx, tt.args.conn, tt.args.typeID)) { + got, err := tt.r.allOfTypeWhere(tt.args.ctx, tt.args.conn, tt.args.typeID, tt.args.qp) + if !tt.wantErr(t, err, fmt.Sprintf("allOfTypeWhere(%v, %v, %v, %v)", tt.args.ctx, tt.args.conn, tt.args.typeID, tt.args.qp)) { return } assertIterResult(t, tt.want, got) @@ -184,3 +210,50 @@ func Test_colAlias(t *testing.T) { }) } } + +func Test_genericRepository_stmtLatestWhere(t *testing.T) { + type args struct { + tid chunk.ChunkType + qp queryParams + } + type testCase[T dbObject] struct { + name string + r genericRepository[T] + args args + want string + want1 []any + } + tests := []testCase[DBWorkspace]{ + { + name: "generates for empty query params", + r: genericRepository[DBWorkspace]{DBWorkspace{}}, + args: args{ + tid: chunk.CWorkspaceInfo, + qp: queryParams{}, + }, + want: "SELECT T.TEAM_ID, MAX(CHUNK_ID) AS CHUNK_ID FROM WORKSPACE AS T JOIN CHUNK AS CH ON CH.ID = T.CHUNK_ID WHERE 1=1 AND CH.TYPE_ID = ? GROUP BY T.TEAM_ID", + want1: []any{chunk.CWorkspaceInfo}, + }, + { + name: "additional predicates", + r: genericRepository[DBWorkspace]{DBWorkspace{}}, + args: args{ + tid: chunk.CWorkspaceInfo, + qp: queryParams{ + Where: "NAME = ?", + Binds: []any{2}, + }, + }, + want: "SELECT T.TEAM_ID, MAX(CHUNK_ID) AS CHUNK_ID FROM WORKSPACE AS T JOIN CHUNK AS CH ON CH.ID = T.CHUNK_ID WHERE 1=1 AND CH.TYPE_ID = ? AND (NAME = ?) GROUP BY T.TEAM_ID", + want1: []any{chunk.CWorkspaceInfo, 2}, + }, + // ordering not supported by this query, so no test. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := tt.r.stmtLatestWhere(tt.args.tid, tt.args.qp) + assert.Equalf(t, tt.want, got, "stmtLatestWhere(%v, %v)", tt.args.tid, tt.args.qp) + assert.Equalf(t, tt.want1, got1, "stmtLatestWhere(%v, %v)", tt.args.tid, tt.args.qp) + }) + } +} diff --git a/internal/chunk/dbproc/repository/repository.go b/internal/chunk/dbproc/repository/repository.go index d9c66fd0..8a6a8050 100644 --- a/internal/chunk/dbproc/repository/repository.go +++ b/internal/chunk/dbproc/repository/repository.go @@ -11,7 +11,7 @@ import ( ) const ( - dbDriver = "sqlite3" + dbDriver = "sqlite" dbTag = "db" ) diff --git a/internal/chunk/dbproc/repository/repository_test.go b/internal/chunk/dbproc/repository/repository_test.go index d337b18f..073af979 100644 --- a/internal/chunk/dbproc/repository/repository_test.go +++ b/internal/chunk/dbproc/repository/repository_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/jmoiron/sqlx" - _ "github.com/mattn/go-sqlite3" "github.com/stretchr/testify/assert" + _ "modernc.org/sqlite" "github.com/rusq/slackdump/v3/internal/chunk" )