mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-21 07:01:41 +03:00
1409 lines
40 KiB
Go
1409 lines
40 KiB
Go
|
package cli_test
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"path/filepath"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/neilotoole/sq/drivers/sqlite3"
|
||
|
|
||
|
"github.com/neilotoole/sq/drivers/sqlserver"
|
||
|
|
||
|
"github.com/neilotoole/sq/drivers/postgres"
|
||
|
"github.com/neilotoole/sq/libsq/source"
|
||
|
|
||
|
"github.com/neilotoole/sq/libsq/core/stringz"
|
||
|
|
||
|
"github.com/samber/lo"
|
||
|
|
||
|
"github.com/neilotoole/sq/testh"
|
||
|
|
||
|
"github.com/neilotoole/sq/cli"
|
||
|
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
|
||
|
"github.com/neilotoole/sq/cli/cobraz"
|
||
|
|
||
|
"github.com/neilotoole/slogt"
|
||
|
"github.com/neilotoole/sq/libsq/core/lg"
|
||
|
|
||
|
"github.com/neilotoole/sq/testh/tutil"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
|
||
|
"github.com/neilotoole/sq/cli/testrun"
|
||
|
"github.com/spf13/cobra"
|
||
|
)
|
||
|
|
||
|
var locSchemes = []string{
|
||
|
"mysql://",
|
||
|
"postgres://",
|
||
|
"sqlite3://",
|
||
|
"sqlserver://",
|
||
|
}
|
||
|
|
||
|
const stdDirective = cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveKeepOrder
|
||
|
|
||
|
func TestCompleteAddLocation_Postgres(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
|
||
|
testCases := []struct {
|
||
|
// args will have "add" prepended
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{""},
|
||
|
want: lo.Union(locSchemes, []string{
|
||
|
"data/", "my/", "my.db", "post/", "post.db", "sqlite/", "sqlite.db",
|
||
|
}),
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"p"},
|
||
|
want: []string{"postgres://", "post/", "post.db"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres:/"},
|
||
|
want: []string{"postgres://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://"},
|
||
|
want: []string{
|
||
|
"postgres://username",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice"},
|
||
|
want: []string{
|
||
|
"postgres://alice@",
|
||
|
"postgres://alice:",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice:"},
|
||
|
want: []string{
|
||
|
"postgres://alice:",
|
||
|
"postgres://alice:@",
|
||
|
"postgres://alice:password@",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/",
|
||
|
"postgres://alice@localhost:5432/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@server"},
|
||
|
want: []string{
|
||
|
"postgres://alice@server/",
|
||
|
"postgres://alice@server:5432/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localho"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localho/",
|
||
|
"postgres://alice@localho:5432/",
|
||
|
"postgres://alice@localhost/",
|
||
|
"postgres://alice@localhost:5432/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/",
|
||
|
"postgres://alice@localhost:5432/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost:"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost:5432/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost:80"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost:80/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/db",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?application_name=",
|
||
|
"postgres://alice@localhost/sakila?channel_binding=",
|
||
|
"postgres://alice@localhost/sakila?connect_timeout=",
|
||
|
"postgres://alice@localhost/sakila?fallback_application_name=",
|
||
|
"postgres://alice@localhost/sakila?gssencmode=",
|
||
|
"postgres://alice@localhost/sakila?sslmode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?ss"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?a=1&b=2&ss"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?a=1&b=2&sslmode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?a=1&b=2&sslmode"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?a=1&b=2&sslmode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode="},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable",
|
||
|
"postgres://alice@localhost/sakila?sslmode=allow",
|
||
|
"postgres://alice@localhost/sakila?sslmode=prefer",
|
||
|
"postgres://alice@localhost/sakila?sslmode=require",
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-ca",
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-full",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=v"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-ca",
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-full",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=verify-"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-ca",
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-full",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=verify-ful"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-full",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=verify-full"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-full&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=verify-full-something"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=verify-full-something&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=disable"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
// Note the extra "?", which apparently is valid
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=disable?"},
|
||
|
want: []string{"postgres://alice@localhost/sakila?sslmode=disable?&"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
// Being that sslmode is already specified, it should not appear a
|
||
|
// second time.
|
||
|
args: []string{"postgres://alice@localhost/sakila?sslmode=disable&"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable&application_name=",
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable&channel_binding=",
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable&connect_timeout=",
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable&fallback_application_name=",
|
||
|
"postgres://alice@localhost/sakila?sslmode=disable&gssencmode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, nil, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCompleteAddLocation_SQLServer(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
|
||
|
testCases := []struct {
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{"sqlse"},
|
||
|
want: []string{"sqlserver://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
|
||
|
{
|
||
|
args: []string{"sqlserver:/"},
|
||
|
want: []string{"sqlserver://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://"},
|
||
|
want: []string{
|
||
|
"sqlserver://username",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=",
|
||
|
"sqlserver://alice@server:1433?database=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server/"},
|
||
|
want: []string{"sqlserver://alice@server/instance?database="},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server/instance"},
|
||
|
want: []string{"sqlserver://alice@server/instance?database="},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=",
|
||
|
"sqlserver://alice@server?ApplicationIntent=",
|
||
|
"sqlserver://alice@server?ServerSPN=",
|
||
|
"sqlserver://alice@server?TrustServerCertificate=",
|
||
|
"sqlserver://alice@server?Workstation+ID=",
|
||
|
"sqlserver://alice@server?app+name=",
|
||
|
"sqlserver://alice@server?certificate=",
|
||
|
"sqlserver://alice@server?connection+timeout=",
|
||
|
"sqlserver://alice@server?dial+timeout=",
|
||
|
"sqlserver://alice@server?encrypt=",
|
||
|
"sqlserver://alice@server?failoverpartner=",
|
||
|
"sqlserver://alice@server?failoverport=",
|
||
|
"sqlserver://alice@server?hostNameInCertificate=",
|
||
|
"sqlserver://alice@server?keepAlive=",
|
||
|
"sqlserver://alice@server?log=",
|
||
|
"sqlserver://alice@server?packet+size=",
|
||
|
"sqlserver://alice@server?protocol=",
|
||
|
"sqlserver://alice@server?tlsmin=",
|
||
|
"sqlserver://alice@server?user+id=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?da"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?database"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?database=sakila"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=sakila&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?database=sakila&tls"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=sakila&tlsmin=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?database=sakila&tlsmin"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=sakila&tlsmin=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@server?database=sakila&tlsmin="},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@server?database=sakila&tlsmin=1.0",
|
||
|
"sqlserver://alice@server?database=sakila&tlsmin=1.1",
|
||
|
"sqlserver://alice@server?database=sakila&tlsmin=1.2",
|
||
|
"sqlserver://alice@server?database=sakila&tlsmin=1.3",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, nil, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCompleteAddLocation_MySQL(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
|
||
|
testCases := []struct {
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{"m"},
|
||
|
want: []string{"mysql://", "my/", "my.db"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"my"},
|
||
|
want: []string{"mysql://", "my/", "my.db"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
// When the input is definitively not a db url, the completion
|
||
|
// switches to the default shell (file) completion.
|
||
|
args: []string{"my/"},
|
||
|
want: []string{},
|
||
|
wantResult: cobra.ShellCompDirectiveDefault,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql"},
|
||
|
want: []string{"mysql://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql:/"},
|
||
|
want: []string{"mysql://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://"},
|
||
|
want: []string{
|
||
|
"mysql://username",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice"},
|
||
|
want: []string{
|
||
|
"mysql://alice@",
|
||
|
"mysql://alice:",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice:"},
|
||
|
want: []string{
|
||
|
"mysql://alice:",
|
||
|
"mysql://alice:@",
|
||
|
"mysql://alice:password@",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/",
|
||
|
"mysql://alice@localhost:3306/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@server"},
|
||
|
want: []string{
|
||
|
"mysql://alice@server/",
|
||
|
"mysql://alice@server:3306/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localho"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localho/",
|
||
|
"mysql://alice@localho:3306/",
|
||
|
"mysql://alice@localhost/",
|
||
|
"mysql://alice@localhost:3306/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/",
|
||
|
"mysql://alice@localhost:3306/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost:"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost:3306/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost:80"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost:80/",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/db",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?allowAllFiles=",
|
||
|
"mysql://alice@localhost/sakila?allowCleartextPasswords=",
|
||
|
"mysql://alice@localhost/sakila?allowFallbackToPlaintext=",
|
||
|
"mysql://alice@localhost/sakila?allowNativePasswords=",
|
||
|
"mysql://alice@localhost/sakila?allowOldPasswords=",
|
||
|
"mysql://alice@localhost/sakila?charset=",
|
||
|
"mysql://alice@localhost/sakila?checkConnLiveness=",
|
||
|
"mysql://alice@localhost/sakila?clientFoundRows=",
|
||
|
"mysql://alice@localhost/sakila?collation=",
|
||
|
"mysql://alice@localhost/sakila?columnsWithAlias=",
|
||
|
"mysql://alice@localhost/sakila?connectionAttributes=",
|
||
|
"mysql://alice@localhost/sakila?interpolateParams=",
|
||
|
"mysql://alice@localhost/sakila?loc=",
|
||
|
"mysql://alice@localhost/sakila?maxAllowedPackage=",
|
||
|
"mysql://alice@localhost/sakila?multiStatements=",
|
||
|
"mysql://alice@localhost/sakila?parseTime=",
|
||
|
"mysql://alice@localhost/sakila?readTimeout=",
|
||
|
"mysql://alice@localhost/sakila?rejectReadOnly=",
|
||
|
"mysql://alice@localhost/sakila?timeout=",
|
||
|
"mysql://alice@localhost/sakila?tls=",
|
||
|
"mysql://alice@localhost/sakila?writeTimeout=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tl"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?a=1&b=2&tl"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?a=1&b=2&tls=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?a=1&b=2&tls"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?a=1&b=2&tls=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tls="},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=false",
|
||
|
"mysql://alice@localhost/sakila?tls=true",
|
||
|
"mysql://alice@localhost/sakila?tls=skip-verify",
|
||
|
"mysql://alice@localhost/sakila?tls=preferred",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tls=s"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=skip-verify",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tls=skip-verify"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=skip-verify&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tls=skip-verify&lo"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=skip-verify&loc=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tls=skip-verify&loc="},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=skip-verify&loc=UTC",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"mysql://alice@localhost/sakila?tls=skip-verify&loc=UTC"},
|
||
|
want: []string{
|
||
|
"mysql://alice@localhost/sakila?tls=skip-verify&loc=UTC&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, nil, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCompleteAddLocation_SQLite3(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
|
||
|
testCases := []struct {
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{"s"},
|
||
|
want: []string{"sqlite3://", "sqlserver://", "sqlite/", "sqlite.db"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite"},
|
||
|
want: []string{"sqlite3://", "sqlite/", "sqlite.db"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite/"},
|
||
|
want: []string{},
|
||
|
wantResult: cobra.ShellCompDirectiveDefault,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"my/my_"},
|
||
|
want: []string{},
|
||
|
wantResult: cobra.ShellCompDirectiveDefault,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3:"},
|
||
|
want: []string{"sqlite3://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3:/"},
|
||
|
want: []string{"sqlite3://"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://"},
|
||
|
want: []string{
|
||
|
"sqlite3://data/",
|
||
|
"sqlite3://my/",
|
||
|
"sqlite3://my.db",
|
||
|
"sqlite3://post/",
|
||
|
"sqlite3://post.db",
|
||
|
"sqlite3://sqlite/",
|
||
|
"sqlite3://sqlite.db",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my"},
|
||
|
want: []string{
|
||
|
"sqlite3://my/",
|
||
|
"sqlite3://my.db",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.d"},
|
||
|
want: []string{"sqlite3://my.db"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db"},
|
||
|
want: []string{"sqlite3://my.db?"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://data/nest1/data.db"},
|
||
|
want: []string{"sqlite3://data/nest1/data.db?", "sqlite3://data/nest1/data.db2"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?"},
|
||
|
want: []string{
|
||
|
"sqlite3://my.db?_auth=",
|
||
|
"sqlite3://my.db?_auth_crypt=",
|
||
|
"sqlite3://my.db?_auth_pass=",
|
||
|
"sqlite3://my.db?_auth_salt=",
|
||
|
"sqlite3://my.db?_auth_user=",
|
||
|
"sqlite3://my.db?_auto_vacuum=",
|
||
|
"sqlite3://my.db?_busy_timeout=",
|
||
|
"sqlite3://my.db?_cache_size=",
|
||
|
"sqlite3://my.db?_case_sensitive_like=",
|
||
|
"sqlite3://my.db?_defer_foreign_keys=",
|
||
|
"sqlite3://my.db?_foreign_keys=",
|
||
|
"sqlite3://my.db?_ignore_check_constraints=",
|
||
|
"sqlite3://my.db?_journal_mode=",
|
||
|
"sqlite3://my.db?_loc=",
|
||
|
"sqlite3://my.db?_locking_mode=",
|
||
|
"sqlite3://my.db?_mutex=",
|
||
|
"sqlite3://my.db?_query_only=",
|
||
|
"sqlite3://my.db?_recursive_triggers=",
|
||
|
"sqlite3://my.db?_secure_delete=",
|
||
|
"sqlite3://my.db?_synchronous=",
|
||
|
"sqlite3://my.db?_txlock=",
|
||
|
"sqlite3://my.db?cache=",
|
||
|
"sqlite3://my.db?mode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_"},
|
||
|
want: []string{"sqlite3://my.db?_locking_mode="},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_mode="},
|
||
|
want: []string{
|
||
|
"sqlite3://my.db?_locking_mode=NORMAL",
|
||
|
"sqlite3://my.db?_locking_mode=EXCLUSIVE",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_mode=NORM"},
|
||
|
want: []string{"sqlite3://my.db?_locking_mode=NORMAL"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_mode=NORMAL"},
|
||
|
want: []string{"sqlite3://my.db?_locking_mode=NORMAL&"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_mode=NORMAL"},
|
||
|
want: []string{"sqlite3://my.db?_locking_mode=NORMAL&"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_mode=NORMAL&ca"},
|
||
|
want: []string{"sqlite3://my.db?_locking_mode=NORMAL&cache="},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my.db?_locking_mode=NORMAL&cache="},
|
||
|
want: []string{
|
||
|
"sqlite3://my.db?_locking_mode=NORMAL&cache=true",
|
||
|
"sqlite3://my.db?_locking_mode=NORMAL&cache=false",
|
||
|
"sqlite3://my.db?_locking_mode=NORMAL&cache=FAST",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, nil, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func testComplete(t testing.TB, from *testrun.TestRun, args ...string) completion {
|
||
|
ctx := lg.NewContext(context.Background(), slogt.New(t))
|
||
|
|
||
|
tr := testrun.New(ctx, t, from)
|
||
|
args = append([]string{"__complete"}, args...)
|
||
|
|
||
|
err := tr.Exec(args...)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
c := parseCompletion(tr)
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// parseCompletion parses the output of cobra "__complete".
|
||
|
// Example output:
|
||
|
//
|
||
|
// @active
|
||
|
// @sakila
|
||
|
// :4
|
||
|
// Completion ended with directive: ShellCompDirectiveNoFileComp
|
||
|
//
|
||
|
// The tr.T test will fail on any error.
|
||
|
func parseCompletion(tr *testrun.TestRun) completion {
|
||
|
c := completion{
|
||
|
stdout: tr.Out.String(),
|
||
|
stderr: tr.ErrOut.String(),
|
||
|
}
|
||
|
|
||
|
lines := strings.Split(strings.TrimSpace(c.stdout), "\n")
|
||
|
require.True(tr.T, len(lines) >= 1)
|
||
|
c.values = lines[:len(lines)-1]
|
||
|
|
||
|
result, err := strconv.Atoi(lines[len(lines)-1][1:])
|
||
|
require.NoError(tr.T, err)
|
||
|
c.result = cobra.ShellCompDirective(result)
|
||
|
|
||
|
c.directives = cobraz.ParseDirectivesLine(c.stderr)
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// completion models the result returned from the cobra "__complete" command.
|
||
|
type completion struct {
|
||
|
stdout string
|
||
|
stderr string
|
||
|
values []string
|
||
|
result cobra.ShellCompDirective
|
||
|
directives []cobra.ShellCompDirective
|
||
|
}
|
||
|
|
||
|
func TestCompleteAddLocation_History_Postgres(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
|
||
|
th := testh.New(t)
|
||
|
tr := testrun.New(th.Context, t, nil)
|
||
|
|
||
|
tr.Add(
|
||
|
source.Source{
|
||
|
Handle: "@src1",
|
||
|
Type: postgres.Type,
|
||
|
Location: "postgres://alice:abc123@dev.acme.com:7777/sakila?application_name=app1&channel_binding=prefer",
|
||
|
},
|
||
|
source.Source{
|
||
|
Handle: "@src2",
|
||
|
Type: postgres.Type,
|
||
|
Location: "postgres://bob:abc123@prod.acme.com:8888/sales?application_name=app2&channel_binding=require",
|
||
|
},
|
||
|
)
|
||
|
|
||
|
testCases := []struct {
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{"postgres://"},
|
||
|
want: []string{
|
||
|
"postgres://alice",
|
||
|
"postgres://bob",
|
||
|
"postgres://username",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://a"},
|
||
|
want: []string{
|
||
|
"postgres://a@",
|
||
|
"postgres://a:",
|
||
|
"postgres://alice@",
|
||
|
"postgres://alice:",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice"},
|
||
|
want: []string{
|
||
|
"postgres://alice@",
|
||
|
"postgres://alice:",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@"},
|
||
|
want: []string{
|
||
|
"postgres://alice@localhost/",
|
||
|
"postgres://alice@localhost:5432/",
|
||
|
"postgres://alice@dev.acme.com:7777/",
|
||
|
"postgres://alice@dev.acme.com:7777/sakila?application_name=app1&channel_binding=prefer",
|
||
|
"postgres://alice@prod.acme.com:8888/",
|
||
|
"postgres://alice@prod.acme.com:8888/sales?application_name=app2&channel_binding=require",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@dev"},
|
||
|
want: []string{
|
||
|
"postgres://alice@dev/",
|
||
|
"postgres://alice@dev:5432/",
|
||
|
"postgres://alice@dev.acme.com:7777/",
|
||
|
"postgres://alice@dev.acme.com:7777/sakila?application_name=app1&channel_binding=prefer",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@dev.acme.com"},
|
||
|
want: []string{
|
||
|
"postgres://alice@dev.acme.com/",
|
||
|
"postgres://alice@dev.acme.com:5432/",
|
||
|
"postgres://alice@dev.acme.com:7777/",
|
||
|
"postgres://alice@dev.acme.com:7777/sakila?application_name=app1&channel_binding=prefer",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@dev.acme.com/"},
|
||
|
want: []string{
|
||
|
"postgres://alice@dev.acme.com/db",
|
||
|
"postgres://alice@dev.acme.com/sakila",
|
||
|
"postgres://alice@dev.acme.com/sales",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@dev.acme.com/sa"},
|
||
|
want: []string{
|
||
|
"postgres://alice@dev.acme.com/sa?",
|
||
|
"postgres://alice@dev.acme.com/sakila",
|
||
|
"postgres://alice@dev.acme.com/sales",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@dev.acme.com/sakila?"},
|
||
|
want: []string{
|
||
|
"postgres://alice@dev.acme.com/sakila?application_name=app1&channel_binding=prefer",
|
||
|
"postgres://alice@dev.acme.com/sakila?application_name=",
|
||
|
"postgres://alice@dev.acme.com/sakila?channel_binding=",
|
||
|
"postgres://alice@dev.acme.com/sakila?connect_timeout=",
|
||
|
"postgres://alice@dev.acme.com/sakila?fallback_application_name=",
|
||
|
"postgres://alice@dev.acme.com/sakila?gssencmode=",
|
||
|
"postgres://alice@dev.acme.com/sakila?sslmode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"postgres://alice@dev.acme.com/sakila?app"},
|
||
|
want: []string{
|
||
|
"postgres://alice@dev.acme.com/sakila?application_name=app1&channel_binding=prefer",
|
||
|
"postgres://alice@dev.acme.com/sakila?application_name=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, tr, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCompleteAddLocation_History_SQLServer(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
|
||
|
th := testh.New(t)
|
||
|
tr := testrun.New(th.Context, t, nil)
|
||
|
|
||
|
tr.Add(
|
||
|
source.Source{
|
||
|
Handle: "@src1",
|
||
|
Type: sqlserver.Type,
|
||
|
Location: "sqlserver://alice:abc123@dev.acme.com:7777?database=sakila&app+name=app1&encrypt=disable",
|
||
|
},
|
||
|
source.Source{
|
||
|
Handle: "@src2",
|
||
|
Type: sqlserver.Type,
|
||
|
Location: "sqlserver://bob:abc123@prod.acme.com:8888?database=sales&app+name=app2&encrypt=true",
|
||
|
},
|
||
|
source.Source{
|
||
|
Handle: "@src3",
|
||
|
Type: sqlserver.Type,
|
||
|
Location: "sqlserver://bob:abc123@prod.acme.com:8888/my_instance?database=sakila",
|
||
|
},
|
||
|
)
|
||
|
|
||
|
testCases := []struct {
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{"sqlserver://"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice",
|
||
|
"sqlserver://bob",
|
||
|
"sqlserver://username",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://a"},
|
||
|
want: []string{
|
||
|
"sqlserver://a@",
|
||
|
"sqlserver://a:",
|
||
|
"sqlserver://alice@",
|
||
|
"sqlserver://alice:",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@",
|
||
|
"sqlserver://alice:",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@localhost?database=",
|
||
|
"sqlserver://alice@localhost:1433?database=",
|
||
|
"sqlserver://alice@dev.acme.com:7777?database=",
|
||
|
"sqlserver://alice@dev.acme.com:7777?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@prod.acme.com:8888/my_instance?database=sakila",
|
||
|
"sqlserver://alice@prod.acme.com:8888?database=",
|
||
|
"sqlserver://alice@prod.acme.com:8888?database=sales&app+name=app2&encrypt=true",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev?database=",
|
||
|
"sqlserver://alice@dev:1433?database=",
|
||
|
"sqlserver://alice@dev.acme.com:7777?database=",
|
||
|
"sqlserver://alice@dev.acme.com:7777?database=sakila&app+name=app1&encrypt=disable",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@prod"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@prod?database=",
|
||
|
"sqlserver://alice@prod:1433?database=",
|
||
|
"sqlserver://alice@prod.acme.com:8888/my_instance?database=sakila",
|
||
|
"sqlserver://alice@prod.acme.com:8888?database=",
|
||
|
"sqlserver://alice@prod.acme.com:8888?database=sales&app+name=app2&encrypt=true",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=",
|
||
|
"sqlserver://alice@dev.acme.com:1433?database=",
|
||
|
"sqlserver://alice@dev.acme.com:7777?database=",
|
||
|
"sqlserver://alice@dev.acme.com:7777?database=sakila&app+name=app1&encrypt=disable",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sales&app+name=app2&encrypt=true",
|
||
|
"sqlserver://alice@dev.acme.com?database=",
|
||
|
"sqlserver://alice@dev.acme.com?ApplicationIntent=",
|
||
|
"sqlserver://alice@dev.acme.com?ServerSPN=",
|
||
|
"sqlserver://alice@dev.acme.com?TrustServerCertificate=",
|
||
|
"sqlserver://alice@dev.acme.com?Workstation+ID=",
|
||
|
"sqlserver://alice@dev.acme.com?app+name=",
|
||
|
"sqlserver://alice@dev.acme.com?certificate=",
|
||
|
"sqlserver://alice@dev.acme.com?connection+timeout=",
|
||
|
"sqlserver://alice@dev.acme.com?dial+timeout=",
|
||
|
"sqlserver://alice@dev.acme.com?encrypt=",
|
||
|
"sqlserver://alice@dev.acme.com?failoverpartner=",
|
||
|
"sqlserver://alice@dev.acme.com?failoverport=",
|
||
|
"sqlserver://alice@dev.acme.com?hostNameInCertificate=",
|
||
|
"sqlserver://alice@dev.acme.com?keepAlive=",
|
||
|
"sqlserver://alice@dev.acme.com?log=",
|
||
|
"sqlserver://alice@dev.acme.com?packet+size=",
|
||
|
"sqlserver://alice@dev.acme.com?protocol=",
|
||
|
"sqlserver://alice@dev.acme.com?tlsmin=",
|
||
|
"sqlserver://alice@dev.acme.com?user+id=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?data"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sales&app+name=app2&encrypt=true",
|
||
|
"sqlserver://alice@dev.acme.com?database=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?database"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sales&app+name=app2&encrypt=true",
|
||
|
"sqlserver://alice@dev.acme.com?database=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?database="},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sales&app+name=app2&encrypt=true",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?database=sa"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sales&app+name=app2&encrypt=true",
|
||
|
"sqlserver://alice@dev.acme.com?database=sa&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?database=saki"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=saki&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?database=sakila"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlserver://alice@dev.acme.com?database=sakila&app"},
|
||
|
want: []string{
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=app1&encrypt=disable",
|
||
|
"sqlserver://alice@dev.acme.com?database=sakila&app+name=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, tr, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestCompleteAddLocation_History_SQLite3(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
wd := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", wd)
|
||
|
src3Loc := "sqlite3://" + wd + "/my.db?cache=FAST"
|
||
|
|
||
|
th := testh.New(t)
|
||
|
tr := testrun.New(th.Context, t, nil)
|
||
|
tr.Add(
|
||
|
source.Source{
|
||
|
Handle: "@src2",
|
||
|
Type: sqlite3.Type,
|
||
|
// Note that this file doesn't actually exist
|
||
|
Location: "sqlite3:///zz_dir1/sqtest/sq/src2.db?mode=rwc&cache=FAST",
|
||
|
},
|
||
|
source.Source{
|
||
|
Handle: "@src1",
|
||
|
Type: sqlite3.Type,
|
||
|
// Note that this file doesn't actually exist
|
||
|
Location: "sqlite3:///zz_dir1/sqtest/sq/src1.db",
|
||
|
},
|
||
|
source.Source{
|
||
|
Handle: "@src3",
|
||
|
Type: sqlite3.Type,
|
||
|
// This file DOES exist
|
||
|
Location: src3Loc,
|
||
|
},
|
||
|
)
|
||
|
|
||
|
testCases := []struct {
|
||
|
args []string
|
||
|
want []string
|
||
|
wantResult cobra.ShellCompDirective
|
||
|
}{
|
||
|
{
|
||
|
args: []string{"sqlite3://"},
|
||
|
want: []string{
|
||
|
src3Loc,
|
||
|
"sqlite3:///zz_dir1/sqtest/sq/src1.db",
|
||
|
"sqlite3:///zz_dir1/sqtest/sq/src2.db?mode=rwc&cache=FAST",
|
||
|
"sqlite3://data/",
|
||
|
"sqlite3://my/",
|
||
|
"sqlite3://my.db",
|
||
|
"sqlite3://post/",
|
||
|
"sqlite3://post.db",
|
||
|
"sqlite3://sqlite/",
|
||
|
"sqlite3://sqlite.db",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3://my"},
|
||
|
want: []string{
|
||
|
"sqlite3://my/",
|
||
|
"sqlite3://my.db",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3:///zz_dir1/sqtest/"},
|
||
|
want: []string{
|
||
|
"sqlite3:///zz_dir1/sqtest/sq/src1.db",
|
||
|
"sqlite3:///zz_dir1/sqtest/sq/src2.db?mode=rwc&cache=FAST",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3:///zz_dir1/sqtest/sq/not_a_dir"},
|
||
|
want: []string{},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3:///zz_dir1/sqtest/sq/src"},
|
||
|
want: []string{
|
||
|
"sqlite3:///zz_dir1/sqtest/sq/src1.db",
|
||
|
"sqlite3:///zz_dir1/sqtest/sq/src2.db?mode=rwc&cache=FAST",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{"sqlite3:///zz_dir1/sqtest/sq/src1.db"},
|
||
|
want: []string{}, // Empty because file doesn't actually exist
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{src3Loc},
|
||
|
want: []string{src3Loc + "&"},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
{
|
||
|
args: []string{src3Loc + "&"},
|
||
|
want: []string{
|
||
|
src3Loc + "&_auth=",
|
||
|
src3Loc + "&_auth_crypt=",
|
||
|
src3Loc + "&_auth_pass=",
|
||
|
src3Loc + "&_auth_salt=",
|
||
|
src3Loc + "&_auth_user=",
|
||
|
src3Loc + "&_auto_vacuum=",
|
||
|
src3Loc + "&_busy_timeout=",
|
||
|
src3Loc + "&_cache_size=",
|
||
|
src3Loc + "&_case_sensitive_like=",
|
||
|
src3Loc + "&_defer_foreign_keys=",
|
||
|
src3Loc + "&_foreign_keys=",
|
||
|
src3Loc + "&_ignore_check_constraints=",
|
||
|
src3Loc + "&_journal_mode=",
|
||
|
src3Loc + "&_loc=",
|
||
|
src3Loc + "&_locking_mode=",
|
||
|
src3Loc + "&_mutex=",
|
||
|
src3Loc + "&_query_only=",
|
||
|
src3Loc + "&_recursive_triggers=",
|
||
|
src3Loc + "&_secure_delete=",
|
||
|
src3Loc + "&_synchronous=",
|
||
|
src3Loc + "&_txlock=",
|
||
|
src3Loc + "&mode=",
|
||
|
},
|
||
|
wantResult: stdDirective,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, strings.Join(tc.args, "_")), func(t *testing.T) {
|
||
|
args := append([]string{"add"}, tc.args...)
|
||
|
got := testComplete(t, tr, args...)
|
||
|
assert.Equal(t, tc.wantResult, got.result, got.directives)
|
||
|
assert.Equal(t, tc.want, got.values)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestParseLoc_stage(t *testing.T) {
|
||
|
testCases := []struct {
|
||
|
loc string
|
||
|
want cli.PlocStage
|
||
|
}{
|
||
|
{"", cli.PlocInit},
|
||
|
{"postgres", cli.PlocInit},
|
||
|
{"postgres:/", cli.PlocInit},
|
||
|
{"postgres://", cli.PlocScheme},
|
||
|
{"postgres://alice", cli.PlocScheme},
|
||
|
{"postgres://alice:", cli.PlocUser},
|
||
|
{"postgres://alice:pass", cli.PlocUser},
|
||
|
{"postgres://alice:pass@", cli.PlocPass},
|
||
|
{"postgres://alice:@", cli.PlocPass},
|
||
|
{"postgres://alice@", cli.PlocPass},
|
||
|
{"postgres://alice@localhost", cli.PlocPass},
|
||
|
{"postgres://alice:@localhost", cli.PlocPass},
|
||
|
{"postgres://alice:pass@localhost", cli.PlocPass},
|
||
|
{"postgres://alice@localhost:", cli.PlocHostname},
|
||
|
{"postgres://alice:@localhost:", cli.PlocHostname},
|
||
|
{"postgres://alice:pass@localhost:", cli.PlocHostname},
|
||
|
{"postgres://alice@localhost:5432", cli.PlocHostname},
|
||
|
{"postgres://alice@localhost:5432/", cli.PlocHost},
|
||
|
{"postgres://alice@localhost:5432/s", cli.PlocHost},
|
||
|
{"postgres://alice@localhost:5432/sakila", cli.PlocHost},
|
||
|
{"postgres://alice@localhost:5432/sakila?", cli.PlocPath},
|
||
|
{"postgres://alice@localhost:5432/sakila?sslmode=verify-ca", cli.PlocPath},
|
||
|
{"postgres://alice:@localhost:5432/sakila?sslmode=verify-ca", cli.PlocPath},
|
||
|
{"postgres://alice:pass@localhost:5432/sakila?sslmode=verify-ca", cli.PlocPath},
|
||
|
{"sqlserver://alice:pass@localhost?", cli.PlocPath},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, tc.loc), func(t *testing.T) {
|
||
|
th := testh.New(t)
|
||
|
ru := th.Run()
|
||
|
|
||
|
gotStage, err := cli.DoTestParseLocStage(t, ru, tc.loc)
|
||
|
require.NoError(t, err)
|
||
|
require.Equal(t, tc.want, gotStage)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestDoCompleteAddLocationFile(t *testing.T) {
|
||
|
tutil.SkipWindows(t, "Shell completion not fully implemented for windows")
|
||
|
|
||
|
absDir := tutil.Chdir(t, filepath.Join("testdata", "add_location"))
|
||
|
t.Logf("Working dir: %s", absDir)
|
||
|
|
||
|
testCases := []struct {
|
||
|
in string
|
||
|
want []string
|
||
|
}{
|
||
|
{"", []string{"data/", "my/", "my.db", "post/", "post.db", "sqlite/", "sqlite.db"}},
|
||
|
{"m", []string{"my/", "my.db"}},
|
||
|
{"my", []string{"my/", "my.db"}},
|
||
|
{"my/", []string{"my/my1.db", "my/my_nest/"}},
|
||
|
{"my/my", []string{"my/my1.db", "my/my_nest/"}},
|
||
|
{"my/my1", []string{"my/my1.db"}},
|
||
|
{"my/my1.db", []string{"my/my1.db"}},
|
||
|
{"my/my_nes", []string{"my/my_nest/"}},
|
||
|
{"my/my_nest", []string{"my/my_nest/"}},
|
||
|
{"my/my_nest/", []string{"my/my_nest/my2.db"}},
|
||
|
{"data/nest1/", []string{"data/nest1/data.db", "data/nest1/data.db2"}},
|
||
|
{"data/nest1/data.db", []string{"data/nest1/data.db", "data/nest1/data.db2"}},
|
||
|
{
|
||
|
absDir + "/",
|
||
|
stringz.PrefixSlice([]string{
|
||
|
"data/", "my/", "my.db", "post/", "post.db", "sqlite/", "sqlite.db",
|
||
|
}, absDir+"/"),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for i, tc := range testCases {
|
||
|
tc := tc
|
||
|
t.Run(tutil.Name(i, tc.in), func(t *testing.T) {
|
||
|
ctx := lg.NewContext(context.Background(), slogt.New(t))
|
||
|
t.Logf("input: %s", tc.in)
|
||
|
t.Logf("want: %s", tc.want)
|
||
|
got := cli.DoCompleteAddLocationFile(ctx, tc.in)
|
||
|
require.Equal(t, tc.want, got)
|
||
|
})
|
||
|
}
|
||
|
}
|