2020-08-06 20:58:47 +03:00
|
|
|
package cli_test
|
|
|
|
|
|
|
|
import (
|
2023-04-22 16:37:07 +03:00
|
|
|
"context"
|
2023-01-01 06:17:44 +03:00
|
|
|
"path/filepath"
|
2022-12-17 01:54:09 +03:00
|
|
|
"testing"
|
|
|
|
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/cli/testrun"
|
2023-05-05 20:41:22 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/options"
|
2022-08-08 22:14:42 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/source"
|
2023-11-21 00:42:38 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/source/drivertype"
|
2022-08-08 22:14:42 +03:00
|
|
|
"github.com/neilotoole/sq/testh"
|
2023-11-20 04:06:36 +03:00
|
|
|
"github.com/neilotoole/sq/testh/proj"
|
|
|
|
"github.com/neilotoole/sq/testh/sakila"
|
2024-01-15 04:45:34 +03:00
|
|
|
"github.com/neilotoole/sq/testh/tu"
|
2020-08-06 20:58:47 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestCmdAdd(t *testing.T) {
|
2023-05-05 20:41:22 +03:00
|
|
|
type query struct {
|
|
|
|
// q is the SLQ query to execute
|
|
|
|
q string
|
|
|
|
wantRows int
|
|
|
|
wantCols int
|
|
|
|
}
|
|
|
|
|
|
|
|
actorDataQuery := &query{
|
|
|
|
q: ".data",
|
|
|
|
wantRows: sakila.TblActorCount,
|
|
|
|
wantCols: len(sakila.TblActorCols()),
|
|
|
|
}
|
2024-01-15 04:45:34 +03:00
|
|
|
_ = actorDataQuery
|
2020-08-06 20:58:47 +03:00
|
|
|
|
|
|
|
testCases := []struct {
|
2024-01-15 04:45:34 +03:00
|
|
|
// Set only one of loc, or locFromHandle, to create
|
|
|
|
// the first arg to "add" cmd.
|
|
|
|
//
|
|
|
|
// loc, when set, will be used directly.
|
|
|
|
loc string
|
|
|
|
// locFromHandle, when set, gets the location from the
|
|
|
|
// config source with the given handle.
|
|
|
|
locFromHandle string
|
|
|
|
|
|
|
|
driver string // --driver flag
|
|
|
|
handle string // --handle flag
|
|
|
|
wantHandle string
|
|
|
|
wantType drivertype.Type
|
|
|
|
wantOptions options.Options
|
|
|
|
wantAddErr bool
|
|
|
|
wantQueryErr bool
|
|
|
|
query *query
|
2020-08-06 20:58:47 +03:00
|
|
|
}{
|
2023-05-05 20:41:22 +03:00
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: " ",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "/",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "../../",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "does/not/exist",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "_",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: ".",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "/",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: "../does/not/exist.csv",
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-31 22:25:40 +03:00
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
2023-05-05 20:41:22 +03:00
|
|
|
handle: "@h1",
|
|
|
|
wantHandle: "@h1",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.CSV,
|
2023-05-05 20:41:22 +03:00
|
|
|
query: actorDataQuery,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
handle: "@h1",
|
|
|
|
wantHandle: "@h1",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.CSV,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
wantHandle: "@actor",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.CSV,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
driver: "csv",
|
|
|
|
wantHandle: "@actor",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.CSV,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
driver: "xlsx",
|
|
|
|
wantHandle: "@actor",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.XLSX,
|
2024-01-15 04:45:34 +03:00
|
|
|
// It's legal to add a CSV file with the xlsx driver.
|
|
|
|
wantAddErr: false,
|
|
|
|
// But it should fail when we try to query it.
|
|
|
|
wantQueryErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-31 22:25:40 +03:00
|
|
|
loc: proj.Abs(sakila.PathTSVActor),
|
2023-05-05 20:41:22 +03:00
|
|
|
handle: "@h1",
|
|
|
|
wantHandle: "@h1",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.TSV,
|
2023-05-05 20:41:22 +03:00
|
|
|
query: actorDataQuery,
|
|
|
|
},
|
|
|
|
{
|
2024-01-31 22:25:40 +03:00
|
|
|
loc: proj.Abs(sakila.PathTSVActorNoHeader),
|
2023-05-05 20:41:22 +03:00
|
|
|
handle: "@h1",
|
|
|
|
wantHandle: "@h1",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.TSV,
|
2023-05-05 20:41:22 +03:00
|
|
|
query: actorDataQuery,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// sqlite can be added both with and without the scheme "sqlite://"
|
|
|
|
loc: "sqlite3://" + proj.Abs(sakila.PathSL3),
|
|
|
|
wantHandle: "@sakila",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.SQLite,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
// with scheme
|
|
|
|
loc: proj.Abs(sakila.PathSL3),
|
|
|
|
wantHandle: "@sakila",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.SQLite,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2024-01-31 22:25:40 +03:00
|
|
|
// without scheme, relative path
|
2023-05-05 20:41:22 +03:00
|
|
|
loc: proj.Rel(sakila.PathSL3),
|
|
|
|
wantHandle: "@sakila",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.SQLite,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
locFromHandle: sakila.Pg,
|
|
|
|
wantHandle: "@sakila",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.Pg,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
locFromHandle: sakila.MS,
|
|
|
|
wantHandle: "@sakila",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.MSSQL,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
locFromHandle: sakila.My,
|
|
|
|
wantHandle: "@sakila",
|
2024-01-25 09:29:55 +03:00
|
|
|
wantType: drivertype.MySQL,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
handle: source.StdinHandle, // reserved handle
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
handle: source.ActiveHandle, // reserved handle
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
handle: source.ScratchHandle, // reserved handle
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
|
|
|
{
|
2024-01-15 04:45:34 +03:00
|
|
|
loc: proj.Abs(sakila.PathCSVActor),
|
|
|
|
handle: source.JoinHandle, // reserved handle
|
|
|
|
wantAddErr: true,
|
2023-05-05 20:41:22 +03:00
|
|
|
},
|
2020-08-06 20:58:47 +03:00
|
|
|
}
|
|
|
|
|
2023-06-18 04:28:11 +03:00
|
|
|
for i, tc := range testCases {
|
2020-08-06 20:58:47 +03:00
|
|
|
tc := tc
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
t.Run(tu.Name(i, tc.wantHandle, tc.loc, tc.locFromHandle, tc.driver), func(t *testing.T) {
|
|
|
|
if tc.locFromHandle != "" {
|
|
|
|
th := testh.New(t)
|
|
|
|
tc.loc = th.Source(tc.locFromHandle).Location
|
|
|
|
}
|
|
|
|
|
2020-08-06 20:58:47 +03:00
|
|
|
args := []string{"add", tc.loc}
|
|
|
|
if tc.handle != "" {
|
|
|
|
args = append(args, "--handle="+tc.handle)
|
|
|
|
}
|
|
|
|
if tc.driver != "" {
|
|
|
|
args = append(args, "--driver="+tc.driver)
|
|
|
|
}
|
|
|
|
|
2024-01-15 04:45:34 +03:00
|
|
|
tr := testrun.New(context.Background(), t, nil)
|
2023-05-19 17:24:18 +03:00
|
|
|
err := tr.Exec(args...)
|
2024-01-15 04:45:34 +03:00
|
|
|
if tc.wantAddErr {
|
2020-08-06 20:58:47 +03:00
|
|
|
require.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// Verify that the src was actually added
|
2023-05-19 17:24:18 +03:00
|
|
|
gotSrc, err := tr.Run.Config.Collection.Get(tc.wantHandle)
|
2020-08-06 20:58:47 +03:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tc.wantHandle, gotSrc.Handle)
|
|
|
|
require.Equal(t, tc.wantType, gotSrc.Type)
|
2023-05-05 20:41:22 +03:00
|
|
|
require.Equal(t, len(tc.wantOptions), len(gotSrc.Options))
|
|
|
|
|
|
|
|
if tc.query == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-11-19 03:05:48 +03:00
|
|
|
err = tr.Reset().Exec(tc.query.q, "--json")
|
2024-01-15 04:45:34 +03:00
|
|
|
if tc.wantQueryErr {
|
|
|
|
require.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
2023-06-18 04:28:11 +03:00
|
|
|
require.NoError(t, err)
|
2023-05-05 20:41:22 +03:00
|
|
|
var results []map[string]any
|
2023-05-19 17:24:18 +03:00
|
|
|
tr.Bind(&results)
|
2023-06-18 04:28:11 +03:00
|
|
|
|
2023-05-05 20:41:22 +03:00
|
|
|
require.Equal(t, tc.query.wantRows, len(results))
|
|
|
|
if tc.query.wantRows > 0 {
|
|
|
|
require.Equal(t, tc.query.wantCols, len(results[0]))
|
|
|
|
}
|
2020-08-06 20:58:47 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2023-01-01 06:17:44 +03:00
|
|
|
|
|
|
|
// TestCmdAdd_SQLite_Path has additional tests for sqlite paths.
|
|
|
|
func TestCmdAdd_SQLite_Path(t *testing.T) {
|
2023-03-19 09:18:54 +03:00
|
|
|
t.Parallel()
|
2023-04-22 16:37:07 +03:00
|
|
|
ctx := context.Background()
|
2023-03-19 09:18:54 +03:00
|
|
|
|
2023-01-01 06:17:44 +03:00
|
|
|
const h1 = `@s1`
|
|
|
|
|
2023-05-19 17:24:18 +03:00
|
|
|
tr := testrun.New(ctx, t, nil)
|
|
|
|
require.NoError(t, tr.Exec("add", "-j", "sqlite3://test.db", "--handle", h1))
|
|
|
|
got := tr.BindMap()
|
2023-01-01 06:17:44 +03:00
|
|
|
|
|
|
|
absPath, err := filepath.Abs("test.db")
|
|
|
|
require.NoError(t, err)
|
2023-01-01 08:35:33 +03:00
|
|
|
absPath = filepath.ToSlash(absPath)
|
2023-01-01 06:17:44 +03:00
|
|
|
|
|
|
|
wantLoc := "sqlite3://" + absPath
|
|
|
|
require.Equal(t, wantLoc, got["location"])
|
|
|
|
}
|
2023-03-19 09:18:54 +03:00
|
|
|
|
|
|
|
func TestCmdAdd_Active(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
const h1, h2, h3, h4 = "@h1", "@h2", "@h3", "@h4"
|
2023-05-22 18:08:14 +03:00
|
|
|
ctx := context.Background()
|
2023-03-19 09:18:54 +03:00
|
|
|
|
|
|
|
// Verify that initially there are no sources.
|
2023-05-19 17:24:18 +03:00
|
|
|
tr := testrun.New(ctx, t, nil)
|
|
|
|
require.NoError(t, tr.Exec("ls"))
|
|
|
|
require.Zero(t, tr.Out.Len())
|
2023-03-19 09:18:54 +03:00
|
|
|
|
|
|
|
// Add a new source. It should become the active source.
|
2023-05-19 17:24:18 +03:00
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("add", proj.Abs(sakila.PathCSVActor), "--handle", h1))
|
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("src", "-j"))
|
|
|
|
m := tr.BindMap()
|
2023-03-19 09:18:54 +03:00
|
|
|
require.Equal(t, h1, m["handle"])
|
|
|
|
|
|
|
|
// Add a second src, without the --active flag. The active src
|
|
|
|
// should remain h1.
|
2023-05-19 17:24:18 +03:00
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("add", proj.Abs(sakila.PathCSVActor), "--handle", h2))
|
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("src", "-j"))
|
|
|
|
m = tr.BindMap()
|
2023-03-19 09:18:54 +03:00
|
|
|
require.Equal(t, h1, m["handle"], "active source should still be %s", h1)
|
|
|
|
|
|
|
|
// Add a third src, this time with the --active flag. The active src
|
|
|
|
// should become h3.
|
2023-05-19 17:24:18 +03:00
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("add", proj.Abs(sakila.PathCSVActor), "--handle", h3, "--active"))
|
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("src", "-j"))
|
|
|
|
m = tr.BindMap()
|
2023-03-19 09:18:54 +03:00
|
|
|
require.Equal(t, h3, m["handle"], "active source now be %s", h3)
|
|
|
|
|
|
|
|
// Same again with a fourth src, but this time using the shorthand -a flag.
|
2023-05-19 17:24:18 +03:00
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("add", proj.Abs(sakila.PathCSVActor), "--handle", h4, "-a"))
|
|
|
|
tr = testrun.New(ctx, t, tr)
|
|
|
|
require.NoError(t, tr.Exec("src", "-j"))
|
|
|
|
m = tr.BindMap()
|
2023-03-19 09:18:54 +03:00
|
|
|
require.Equal(t, h4, m["handle"], "active source now be %s", h4)
|
|
|
|
}
|