sq/libsq/source/source_test.go
Neil O'Toole e5db7990bc
#199: Kill RunContext.Log (#211)
* refactor: RunContext.Log is dead

* Fiddling with options

* Changed default max conns to zero (no limit)

* Linting
2023-04-30 08:18:56 -06:00

564 lines
15 KiB
Go

package source_test
import (
"testing"
"github.com/neilotoole/slogt"
"github.com/neilotoole/sq/drivers/sqlite3"
"github.com/neilotoole/sq/libsq/core/lg/lga"
"github.com/neilotoole/sq/libsq/core/options"
"github.com/neilotoole/sq/testh/proj"
"github.com/neilotoole/sq/testh/tutil"
"github.com/stretchr/testify/require"
"github.com/neilotoole/sq/libsq/source"
)
const (
prodGroup = "prod"
devGroup = "dev"
devCustGroup = "dev/customer"
)
// newSource returns a new source with handle, pointing to
// the sqlite sakila.db.
func newSource(handle string) *source.Source {
return &source.Source{
Handle: handle,
Type: sqlite3.Type,
Location: proj.Abs("drivers/sqlite3/testdata/sakila.db"),
}
}
func TestCollection_Groups(t *testing.T) {
srcs := []*source.Source{
{Handle: "@db1", Location: "0"},
{Handle: "@prod/db1", Location: "1"},
{Handle: "@prod/sub1/db1", Location: "2"},
{Handle: "@prod/sub1/db2", Location: "3"},
{Handle: "@prod/sub1/sub2/sub3/db2", Location: "4"},
{Handle: "@prod/sub1/sub2/sub4/sub5/db", Location: "5"},
{Handle: "@staging/sub1/sub2/db", Location: "6"},
{Handle: "@dev/db", Location: "7"},
}
require.Equal(t, srcs[0].Group(), "")
require.Equal(t, srcs[1].Group(), "prod")
require.Equal(t, srcs[2].Group(), "prod/sub1")
require.Equal(t, srcs[5].Group(), "prod/sub1/sub2/sub4/sub5")
require.Equal(t, srcs[7].Group(), "dev")
wantGroups := []string{
source.RootGroup,
"dev",
"prod",
"prod/sub1",
"prod/sub1/sub2",
"prod/sub1/sub2/sub3",
"prod/sub1/sub2/sub4",
"prod/sub1/sub2/sub4/sub5",
"staging",
"staging/sub1",
"staging/sub1/sub2",
}
coll := &source.Collection{}
gotGroup := coll.ActiveGroup()
require.Equal(t, source.RootGroup, gotGroup)
for i := range srcs {
require.NoError(t, coll.Add(srcs[i]))
}
for _, src := range srcs {
require.True(t, coll.IsExistingSource(src.Handle))
gotSrc, err := coll.Get(src.Handle)
require.NoError(t, err)
require.Equal(t, *src, *gotSrc)
}
gotGroups := coll.Groups()
require.EqualValues(t, wantGroups, gotGroups)
gotErr := coll.SetActiveGroup("not_a_group")
require.Error(t, gotErr)
groupTest := map[string]int{
"": len(srcs),
"prod": 5,
"prod/sub1": 4,
"prod/sub1/sub2/sub4/sub5": 1,
"dev": 1,
"prod/sub1/sub2": 2,
}
for g, wantCount := range groupTest {
gotSrcs, err := coll.SourcesInGroup(g)
require.NoError(t, err)
require.Equal(t, wantCount, len(gotSrcs))
}
}
func TestRedactedLocation(t *testing.T) {
testCases := []struct {
loc string
want string
}{
{
loc: "/path/to/sqlite.db",
want: "/path/to/sqlite.db",
},
{
loc: "/path/to/data.xlsx",
want: "/path/to/data.xlsx",
},
{
loc: "https://path/to/data.xlsx",
want: "https://path/to/data.xlsx",
},
{
loc: "http://path/to/data.xlsx",
want: "http://path/to/data.xlsx",
},
{
loc: "sqlserver://sq:p_ssW0rd@localhost?database=sqtest",
want: "sqlserver://sq:xxxxx@localhost?database=sqtest",
},
{
loc: "postgres://sq:p_ssW0rd@localhost/sqtest?sslmode=disable",
want: "postgres://sq:xxxxx@localhost/sqtest?sslmode=disable",
},
{
loc: "mysql://sq:p_ssW0rd@localhost:3306/sqtest",
want: "mysql://sq:xxxxx@localhost:3306/sqtest",
},
{
loc: "sqlite3:///path/to/sqlite.db",
want: "sqlite3:///path/to/sqlite.db",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tutil.Name(tc.loc), func(t *testing.T) {
src := &source.Source{Location: tc.loc}
got := src.RedactedLocation()
t.Logf("%s --> %s", src.Location, got)
require.Equal(t, tc.want, got)
})
}
}
func TestShortLocation(t *testing.T) {
testCases := []struct {
name string
loc string
want string
}{
{
name: "sqlite3_scheme",
loc: "sqlite3:///path/to/sqlite.db",
want: "sqlite.db",
},
{
name: "sqlite3",
loc: "/path/to/sqlite.db",
want: "sqlite.db",
},
{
name: "xlsx",
loc: "/path/to/data.xlsx",
want: "data.xlsx",
},
{
name: "https",
loc: "https://path/to/data.xlsx",
want: "data.xlsx",
},
{
name: "http",
loc: "http://path/to/data.xlsx",
want: "data.xlsx",
},
{
name: "sqlserver",
loc: "sqlserver://sq:p_ssw0rd@localhost?database=sqtest",
want: "sq@localhost/sqtest",
},
{
name: "postgres",
loc: "postgres://sq:p_ssW0rd@localhost/sqtest?sslmode=disable",
want: "sq@localhost/sqtest",
},
{
name: "mysql",
loc: "mysql://sq:p_ssW0rd@localhost:3306/sqtest",
want: "sq@localhost:3306/sqtest",
},
{
name: "mysql",
loc: "mysql://sq:p_ssW0rd@localhost/sqtest",
want: "sq@localhost/sqtest",
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
got := source.ShortLocation(tc.loc)
t.Logf("%s --> %s", tc.loc, got)
require.Equal(t, tc.want, got)
})
}
}
func TestContains(t *testing.T) {
src1 := &source.Source{Handle: "@src1"}
src2 := &source.Source{Handle: "@src2"}
var srcs []*source.Source
require.False(t, source.Contains(nil, (*source.Source)(nil)))
require.False(t, source.Contains(nil, ""))
require.False(t, source.Contains(srcs, src1.Handle))
srcs = make([]*source.Source, 0)
require.False(t, source.Contains(srcs, src1.Handle))
srcs = append(srcs, src1)
require.True(t, source.Contains(srcs, src1))
require.True(t, source.Contains(srcs, src1.Handle))
require.False(t, source.Contains(srcs, src2))
require.False(t, source.Contains(srcs, src2.Handle))
srcs = append(srcs, src2)
require.True(t, source.Contains(srcs, src2))
require.True(t, source.Contains(srcs, src2.Handle))
}
func TestCollection_Active(t *testing.T) {
coll := &source.Collection{}
activeSrc := coll.Active()
require.Nil(t, activeSrc)
require.Equal(t, source.RootGroup, coll.ActiveGroup())
require.Error(t, coll.SetActiveGroup("non_exist"))
sakilaSrc := newSource("@sakila")
// Test that the active group and
require.NoError(t, coll.Add(sakilaSrc))
gotSrc, err := coll.Get(sakilaSrc.Handle)
require.NoError(t, err)
require.Equal(t, sakilaSrc, gotSrc)
require.Equal(t, source.RootGroup, coll.ActiveGroup(),
"active group should not have changed due to adding a source")
require.Nil(t, coll.Active())
// Test setting the active source
gotSrc, err = coll.SetActive(sakilaSrc.Handle, false)
require.NoError(t, err)
require.Equal(t, sakilaSrc, gotSrc)
require.Equal(t, gotSrc, coll.Active())
// Test removing the active source
require.NoError(t, coll.Remove(coll.ActiveHandle()))
require.Nil(t, coll.Active())
// Test group
sakilaProdSrc := newSource("@prod/sakila")
require.NoError(t, coll.Add(sakilaProdSrc))
require.Equal(t, source.RootGroup, coll.ActiveGroup(),
"adding a grouped src should not set the active group")
gotSrc, err = coll.SetActive(sakilaProdSrc.Handle, false)
require.NoError(t, err)
require.Equal(t, sakilaProdSrc, gotSrc)
require.Equal(t, source.RootGroup, coll.ActiveGroup(),
"setting active src should not set active group")
require.NoError(t, coll.SetActiveGroup(prodGroup))
require.Equal(t, prodGroup, coll.ActiveGroup())
gotSrcs, err := coll.RemoveGroup(prodGroup)
require.NoError(t, err)
require.Equal(t, sakilaProdSrc, gotSrcs[0])
require.Equal(t, source.RootGroup, coll.ActiveGroup(),
"active group should have been reset to root")
require.False(t, coll.IsExistingGroup(prodGroup))
require.Empty(t, coll.Sources())
}
func TestCollection_RenameGroup_toRoot(t *testing.T) {
coll := &source.Collection{}
gotSrcs, err := coll.RenameGroup(source.RootGroup, prodGroup)
require.Error(t, err, "can't rename root group")
require.Nil(t, gotSrcs)
src := newSource("@prod/sakila")
originalHandle := src.Handle
require.NoError(t, coll.Add(src))
gotSrcs, err = coll.SourcesInGroup(prodGroup)
require.NoError(t, err)
require.Len(t, gotSrcs, 1)
require.Equal(t, src, gotSrcs[0])
// Rename "prod" group to root effectively moves all prod sources
// into root. The prod group will cease to exist.
gotSrcs, err = coll.RenameGroup(prodGroup, source.RootGroup)
require.NoError(t, err)
require.Len(t, gotSrcs, 1)
require.Equal(t, source.RootGroup, coll.ActiveGroup())
require.Equal(t, "@sakila", src.Handle, "src should have new handle")
require.False(t, coll.IsExistingGroup(prodGroup))
gotSrc, err := coll.Get(originalHandle)
require.Error(t, err, "original handle no longer exists")
require.Nil(t, gotSrc)
gotSrcs, err = coll.SourcesInGroup(prodGroup)
require.Error(t, err, "group should not not exist")
require.Empty(t, gotSrcs)
gotSrc, err = coll.Get("@sakila")
require.NoError(t, err, "should be available via new handle")
require.Equal(t, src.Location, gotSrc.Location)
gotSrcs, err = coll.SourcesInGroup(source.RootGroup)
require.NoError(t, err)
require.Len(t, gotSrcs, 1)
require.Equal(t, src, gotSrcs[0])
// Do the same as above, but rename "prod" group to "prod/customer".
}
func TestCollection_RenameGroup_toOther(t *testing.T) {
coll := &source.Collection{}
src := newSource("@prod/sakila")
originalHandle := src.Handle
require.NoError(t, coll.Add(src))
// Rename "prod" group to "dev/customer" effectively moves all prod sources
// into "dev/customer". The prod group will cease to exist.
gotSrcs, err := coll.RenameGroup(prodGroup, devCustGroup)
require.NoError(t, err)
require.Len(t, gotSrcs, 1)
require.Equal(t, source.RootGroup, coll.ActiveGroup())
require.Equal(t, "@dev/customer/sakila", src.Handle,
"src should have new handle")
require.False(t, coll.IsExistingGroup(prodGroup))
gotSrc, err := coll.Get(originalHandle)
require.Error(t, err, "original handle no longer exists")
require.Nil(t, gotSrc)
gotSrcs, err = coll.SourcesInGroup(prodGroup)
require.Error(t, err, "group should not not exist")
require.Empty(t, gotSrcs)
gotSrc, err = coll.Get("@dev/customer/sakila")
require.NoError(t, err, "should be available via new handle")
require.Equal(t, src.Location, gotSrc.Location)
gotSrcs, err = coll.SourcesInGroup(devCustGroup)
require.NoError(t, err)
require.Len(t, gotSrcs, 1)
require.Equal(t, src, gotSrcs[0])
}
func TestCollection_Add_conflictsWithGroup(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@prod/sakila")
require.NoError(t, coll.Add(src1))
require.True(t, coll.IsExistingGroup(prodGroup))
src2 := newSource("@prod")
require.Error(t, coll.Add(src2), "handle conflicts with existing group")
}
func TestCollection_Add_groupConflictsWithSource(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@sakila")
require.NoError(t, coll.Add(src1))
src2 := newSource("@sakila/sakiladb")
require.Error(t, coll.Add(src2), "handle group (sakila) conflicts with source @sakila")
}
func TestCollection_RenameGroup(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@prod/sakila")
require.NoError(t, coll.Add(src1))
gotSrcs, err := coll.RenameGroup(devGroup, prodGroup)
require.Error(t, err, "group dev does not exist")
require.Nil(t, gotSrcs)
gotSrcs, err = coll.RenameGroup(prodGroup, devGroup)
require.NoError(t, err)
require.Equal(t, gotSrcs[0].Handle, "@dev/sakila")
}
func TestCollection_RenameGroup_conflictsWithSource(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@sakila")
require.NoError(t, coll.Add(src1))
src2 := newSource("@prod/db")
require.NoError(t, coll.Add(src2))
_, err := coll.RenameGroup("prod", "sakila")
require.Error(t, err, "should be a conflict error")
}
func TestCollection_MoveHandleToGroup(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@sakila")
require.NoError(t, coll.Add(src1))
gotSrc, err := coll.MoveHandleToGroup(src1.Handle, "/")
// This is effectively no-op
require.NoError(t, err)
require.Equal(t, src1, gotSrc)
gotSrc, err = coll.MoveHandleToGroup(src1.Handle, prodGroup)
require.NoError(t, err, "it is legal to move a handle to a non-existing group")
require.Equal(t, "@prod/sakila", gotSrc.Handle)
require.Equal(t, prodGroup, gotSrc.Group())
}
func TestCollection_MoveHandleToGroup_conflictsWithExistingSource(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@sakila")
require.NoError(t, coll.Add(src1))
src2 := newSource("@prod/db")
require.NoError(t, coll.Add(src2))
gotSrc, err := coll.MoveHandleToGroup(src1.Handle, "sakila")
// This is effectively no-op
require.Error(t, err, "group 'sakila' should conflict with handle @sakila")
require.Nil(t, gotSrc)
}
func TestCollection_RenameSource(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@sakila")
require.NoError(t, coll.Add(src1))
gotSrc, err := coll.RenameSource(src1.Handle, "@sakila2")
require.NoError(t, err)
require.Equal(t, "@sakila2", gotSrc.Handle)
require.Equal(t, src1, gotSrc)
}
func TestCollection_RenameSource_conflictsWithExistingHandle(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@prod/sakila")
require.NoError(t, coll.Add(src1))
src2 := newSource("@dev/sakila")
require.NoError(t, coll.Add(src2))
gotSrc, err := coll.RenameSource(src2.Handle, src1.Handle)
require.Error(t, err)
require.Nil(t, gotSrc)
}
func TestCollection_RenameSource_conflictsWithExistingGroup(t *testing.T) {
coll := &source.Collection{}
src1 := newSource("@prod/sakila")
require.NoError(t, coll.Add(src1))
src2 := newSource("@dev/sakila")
require.NoError(t, coll.Add(src2))
gotSrc, err := coll.RenameSource(src1.Handle, "/")
require.Error(t, err)
require.Nil(t, gotSrc)
gotSrc, err = coll.RenameSource(src1.Handle, "@prod")
require.Error(t, err)
require.Nil(t, gotSrc)
}
func TestCollection_Tree(t *testing.T) {
coll := &source.Collection{}
handles := []string{
"@sakila_csv",
"@sakila_tsv",
"@dev/db1",
"@dev/pg/db1",
"@dev/pg/db2",
"@dev/pg/db3",
"@staging/db1",
"@prod/pg/db1",
"@prod/pg/db2",
"@prod/pg/backup/db1",
"@prod/pg/backup/db2",
}
for _, handle := range handles {
require.NoError(t, coll.Add(newSource(handle)))
}
gotSrcs := coll.Sources()
require.Len(t, gotSrcs, 11)
gotGroupNames := coll.Groups()
require.Len(t, gotGroupNames, 7)
gotTree, err := coll.Tree(source.RootGroup)
require.NoError(t, err)
directSrcCount, allSrcCount, directGroupCount, allGroupCount := gotTree.Counts()
require.Equal(t, 2, directSrcCount)
require.Equal(t, directSrcCount, len(gotTree.Sources))
require.Equal(t, 11, allSrcCount)
require.Equal(t, 3, directGroupCount)
require.Equal(t, directGroupCount, len(gotTree.Groups))
require.Equal(t, 6, allGroupCount)
require.True(t, gotTree.Active, "root group is active")
require.False(t, gotTree.Groups[0].Active)
// Try with a subgroup
gotTree, err = coll.Tree("dev")
require.NoError(t, err)
directSrcCount, allSrcCount, directGroupCount, allGroupCount = gotTree.Counts()
require.Equal(t, 1, directSrcCount)
require.Equal(t, directSrcCount, len(gotTree.Sources))
require.Equal(t, 4, allSrcCount)
require.Equal(t, 1, directGroupCount)
require.Equal(t, directGroupCount, len(gotTree.Groups))
require.Equal(t, 1, allGroupCount)
require.False(t, gotTree.Active)
}
func TestSource_LogValue(t *testing.T) {
log := slogt.New(t)
src := &source.Source{
Handle: "@sakila",
Type: sqlite3.Type,
Location: "/tmp/sakila.db",
Options: nil,
}
log.Debug("src with nil Options", lga.Src, src)
src.Options = options.Options{"a": 1, "b": true, "c": "hello"}
log.Debug("src with non-nil Options", lga.Src, src)
}