mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-26 01:34:43 +03:00
a5c4f9d6bd
* Refactor tablew md writer * wip: cmd inspect -v * cmd inspect output improved * cmd inspect tests * cmd inspect: help text
326 lines
9.0 KiB
Go
326 lines
9.0 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/samber/lo"
|
|
|
|
"github.com/neilotoole/sq/cli/flag"
|
|
|
|
"github.com/neilotoole/sq/cli/output/format"
|
|
"github.com/neilotoole/sq/libsq/core/ioz"
|
|
|
|
"github.com/neilotoole/sq/drivers/postgres"
|
|
|
|
"github.com/neilotoole/sq/cli/testrun"
|
|
|
|
"github.com/neilotoole/sq/testh/tutil"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/neilotoole/sq/drivers/csv"
|
|
"github.com/neilotoole/sq/drivers/sqlite3"
|
|
"github.com/neilotoole/sq/libsq/source"
|
|
"github.com/neilotoole/sq/testh"
|
|
"github.com/neilotoole/sq/testh/proj"
|
|
"github.com/neilotoole/sq/testh/sakila"
|
|
)
|
|
|
|
// TestCmdInspect_json_yaml tests "sq inspect" for
|
|
// the JSON and YAML formats.
|
|
func TestCmdInspect_json_yaml(t *testing.T) {
|
|
testCases := []struct {
|
|
handle string
|
|
wantTbls []string
|
|
}{
|
|
{sakila.CSVActor, []string{source.MonotableName}},
|
|
{sakila.TSVActor, []string{source.MonotableName}},
|
|
{sakila.XLSX, sakila.AllTbls()},
|
|
{sakila.SL3, sakila.AllTbls()},
|
|
{sakila.Pg, lo.Without(sakila.AllTbls(), sakila.TblFilmText)}, // pg doesn't have film_text
|
|
{sakila.My, sakila.AllTbls()},
|
|
{sakila.MS, sakila.AllTbls()},
|
|
}
|
|
|
|
testFormats := []struct {
|
|
format format.Format
|
|
unmarshalFn func(data []byte, v any) error
|
|
}{
|
|
{format.JSON, json.Unmarshal},
|
|
{format.YAML, ioz.UnmarshallYAML},
|
|
}
|
|
|
|
for _, tf := range testFormats {
|
|
tf := tf
|
|
t.Run(tf.format.String(), func(t *testing.T) {
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
t.Run(tc.handle, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tutil.SkipWindowsIf(t, tc.handle == sakila.XLSX, "XLSX too slow on windows workflow")
|
|
|
|
th := testh.New(t)
|
|
src := th.Source(tc.handle)
|
|
|
|
tr := testrun.New(th.Context, t, nil).Hush().Add(*src)
|
|
err := tr.Exec("inspect", fmt.Sprintf("--%s", tf.format))
|
|
require.NoError(t, err)
|
|
|
|
srcMeta := &source.Metadata{}
|
|
require.NoError(t, tf.unmarshalFn(tr.Out.Bytes(), srcMeta))
|
|
require.Equal(t, src.Type, srcMeta.Driver)
|
|
require.Equal(t, src.Handle, srcMeta.Handle)
|
|
require.Equal(t, source.RedactLocation(src.Location), srcMeta.Location)
|
|
|
|
gotTableNames := srcMeta.TableNames()
|
|
|
|
for _, wantTblName := range tc.wantTbls {
|
|
if src.Type == postgres.Type && wantTblName == "film_text" {
|
|
// Postgres sakila DB doesn't have film_text for some reason
|
|
continue
|
|
}
|
|
require.Contains(t, gotTableNames, wantTblName)
|
|
}
|
|
|
|
t.Run("inspect_table", func(t *testing.T) {
|
|
for _, tblName := range gotTableNames {
|
|
tblName := tblName
|
|
t.Run(tblName, func(t *testing.T) {
|
|
tutil.SkipShort(t, true)
|
|
t.Logf("Test: sq inspect .tbl")
|
|
tr2 := testrun.New(th.Context, t, tr)
|
|
err := tr2.Exec("inspect", "."+tblName, fmt.Sprintf("--%s", tf.format))
|
|
require.NoError(t, err)
|
|
tblMeta := &source.TableMetadata{}
|
|
require.NoError(t, tf.unmarshalFn(tr2.Out.Bytes(), tblMeta))
|
|
require.Equal(t, tblName, tblMeta.Name)
|
|
require.True(t, len(tblMeta.Columns) > 0)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("inspect_overview", func(t *testing.T) {
|
|
t.Logf("Test: sq inspect @src --overview")
|
|
tr2 := testrun.New(th.Context, t, tr)
|
|
err := tr2.Exec(
|
|
"inspect",
|
|
tc.handle,
|
|
fmt.Sprintf("--%s", flag.InspectOverview),
|
|
fmt.Sprintf("--%s", tf.format),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
srcMeta := &source.Metadata{}
|
|
require.NoError(t, tf.unmarshalFn(tr2.Out.Bytes(), srcMeta))
|
|
require.Equal(t, src.Type, srcMeta.Driver)
|
|
require.Equal(t, src.Handle, srcMeta.Handle)
|
|
require.Nil(t, srcMeta.Tables)
|
|
require.Zero(t, srcMeta.TableCount)
|
|
require.Zero(t, srcMeta.ViewCount)
|
|
})
|
|
|
|
t.Run("inspect_dbprops", func(t *testing.T) {
|
|
t.Logf("Test: sq inspect @src --dbprops")
|
|
tr2 := testrun.New(th.Context, t, tr)
|
|
err := tr2.Exec(
|
|
"inspect",
|
|
tc.handle,
|
|
fmt.Sprintf("--%s", flag.InspectDBProps),
|
|
fmt.Sprintf("--%s", tf.format),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
props := map[string]any{}
|
|
require.NoError(t, tf.unmarshalFn(tr2.Out.Bytes(), &props))
|
|
require.NotEmpty(t, props)
|
|
})
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCmdInspect_text tests "sq inspect" for
|
|
// the text format.
|
|
func TestCmdInspect_text(t *testing.T) { //nolint:tparallel
|
|
testCases := []struct {
|
|
handle string
|
|
wantTbls []string
|
|
}{
|
|
{sakila.CSVActor, []string{source.MonotableName}},
|
|
{sakila.TSVActor, []string{source.MonotableName}},
|
|
{sakila.XLSX, sakila.AllTbls()},
|
|
{sakila.SL3, sakila.AllTbls()},
|
|
{sakila.Pg, lo.Without(sakila.AllTbls(), sakila.TblFilmText)}, // pg doesn't have film_text
|
|
{sakila.My, sakila.AllTbls()},
|
|
{sakila.MS, sakila.AllTbls()},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
t.Run(tc.handle, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tutil.SkipWindowsIf(t, tc.handle == sakila.XLSX, "XLSX too slow on windows workflow")
|
|
|
|
th := testh.New(t)
|
|
src := th.Source(tc.handle)
|
|
|
|
tr := testrun.New(th.Context, t, nil).Hush().Add(*src)
|
|
err := tr.Exec("inspect", fmt.Sprintf("--%s", format.Text))
|
|
require.NoError(t, err)
|
|
|
|
output := tr.Out.String()
|
|
require.Contains(t, output, src.Type)
|
|
require.Contains(t, output, src.Handle)
|
|
require.Contains(t, output, source.RedactLocation(src.Location))
|
|
|
|
for _, wantTblName := range tc.wantTbls {
|
|
if src.Type == postgres.Type && wantTblName == "film_text" {
|
|
// Postgres sakila DB doesn't have film_text for some reason
|
|
continue
|
|
}
|
|
require.Contains(t, output, wantTblName)
|
|
}
|
|
|
|
t.Run("inspect_table", func(t *testing.T) {
|
|
for _, tblName := range tc.wantTbls {
|
|
tblName := tblName
|
|
t.Run(tblName, func(t *testing.T) {
|
|
tutil.SkipShort(t, true)
|
|
t.Logf("Test: sq inspect .tbl")
|
|
tr2 := testrun.New(th.Context, t, tr)
|
|
err := tr2.Exec("inspect", "."+tblName, fmt.Sprintf("--%s", format.Text))
|
|
require.NoError(t, err)
|
|
|
|
output := tr2.Out.String()
|
|
require.Contains(t, output, tblName)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("inspect_overview", func(t *testing.T) {
|
|
t.Logf("Test: sq inspect @src --overview")
|
|
tr2 := testrun.New(th.Context, t, tr)
|
|
err := tr2.Exec(
|
|
"inspect",
|
|
tc.handle,
|
|
fmt.Sprintf("--%s", flag.InspectOverview),
|
|
fmt.Sprintf("--%s", format.Text),
|
|
)
|
|
require.NoError(t, err)
|
|
output := tr2.Out.String()
|
|
require.Contains(t, output, src.Type)
|
|
require.Contains(t, output, src.Handle)
|
|
require.Contains(t, output, source.RedactLocation(src.Location))
|
|
})
|
|
|
|
t.Run("inspect_dbprops", func(t *testing.T) {
|
|
t.Logf("Test: sq inspect @src --dbprops")
|
|
tr2 := testrun.New(th.Context, t, tr)
|
|
err := tr2.Exec(
|
|
"inspect",
|
|
tc.handle,
|
|
fmt.Sprintf("--%s", flag.InspectDBProps),
|
|
fmt.Sprintf("--%s", format.Text),
|
|
)
|
|
require.NoError(t, err)
|
|
output := tr2.Out.String()
|
|
require.NotEmpty(t, output)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCmdInspect_smoke(t *testing.T) {
|
|
th := testh.New(t)
|
|
src := th.Source(sakila.SL3)
|
|
|
|
tr := testrun.New(th.Context, t, nil)
|
|
err := tr.Exec("inspect")
|
|
require.Error(t, err, "should fail because no active src")
|
|
|
|
tr = testrun.New(th.Context, t, nil)
|
|
tr.Add(*src) // now have an active src
|
|
|
|
err = tr.Exec("inspect", "--json")
|
|
require.NoError(t, err, "should pass because there is an active src")
|
|
|
|
md := &source.Metadata{}
|
|
require.NoError(t, json.Unmarshal(tr.Out.Bytes(), md))
|
|
require.Equal(t, sqlite3.Type, md.Driver)
|
|
require.Equal(t, sakila.SL3, md.Handle)
|
|
require.Equal(t, src.RedactedLocation(), md.Location)
|
|
require.Equal(t, sakila.AllTblsViews(), md.TableNames())
|
|
|
|
// Try one more source for good measure
|
|
tr = testrun.New(th.Context, t, nil)
|
|
src = th.Source(sakila.CSVActor)
|
|
tr.Add(*src)
|
|
|
|
err = tr.Exec("inspect", "--json", src.Handle)
|
|
require.NoError(t, err)
|
|
|
|
md = &source.Metadata{}
|
|
require.NoError(t, json.Unmarshal(tr.Out.Bytes(), md))
|
|
require.Equal(t, csv.TypeCSV, md.Driver)
|
|
require.Equal(t, sakila.CSVActor, md.Handle)
|
|
require.Equal(t, src.Location, md.Location)
|
|
require.Equal(t, []string{source.MonotableName}, md.TableNames())
|
|
}
|
|
|
|
func TestCmdInspect_stdin(t *testing.T) {
|
|
testCases := []struct {
|
|
fpath string
|
|
wantErr bool
|
|
wantType source.DriverType
|
|
wantTbls []string
|
|
}{
|
|
{
|
|
fpath: proj.Abs(sakila.PathCSVActor),
|
|
wantType: csv.TypeCSV,
|
|
wantTbls: []string{source.MonotableName},
|
|
},
|
|
{
|
|
fpath: proj.Abs(sakila.PathTSVActor),
|
|
wantType: csv.TypeTSV,
|
|
wantTbls: []string{source.MonotableName},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
tc := tc
|
|
|
|
t.Run(tutil.Name(tc.fpath), func(t *testing.T) {
|
|
ctx := context.Background()
|
|
f, err := os.Open(tc.fpath) // No need to close f
|
|
require.NoError(t, err)
|
|
|
|
tr := testrun.New(ctx, t, nil)
|
|
tr.Run.Stdin = f
|
|
|
|
err = tr.Exec("inspect", "--json")
|
|
if tc.wantErr {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err, "should read from stdin")
|
|
|
|
md := &source.Metadata{}
|
|
require.NoError(t, json.Unmarshal(tr.Out.Bytes(), md))
|
|
require.Equal(t, tc.wantType, md.Driver)
|
|
require.Equal(t, source.StdinHandle, md.Handle)
|
|
require.Equal(t, source.StdinHandle, md.Location)
|
|
require.Equal(t, tc.wantTbls, md.TableNames())
|
|
})
|
|
}
|
|
}
|