diff --git a/README.md b/README.md index 11221af..33e9c5c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # Superstack CLI -`superstack` is the command line interface to Superstack: log in, claim -devices, upload Lua code, and stream logs from your fleet. It is a single -static binary for managing Superstack from a terminal. +`superstack` is the command line interface to Superstack: log in, create +fleets, claim devices, and manage who can reach them. It is a single static +binary for managing Superstack from a terminal. + +Uploading Lua code and streaming logs are not available yet. ## Install @@ -60,11 +62,11 @@ static binary for managing Superstack from a terminal. ./superstack ``` -1. Run the fast tests while you work. The login tests wait real seconds for - the poll interval, and `-short` skips them: +1. Run the tests while you work. Go caches per package, so a change to one + part leaves every other part cached: ```sh - CGO_ENABLED=0 go test -short ./... + CGO_ENABLED=0 go test ./... ``` 1. Run every check before opening a pull request: diff --git a/internal/account/account.go b/internal/account/account.go index 6b18a44..fda4769 100644 --- a/internal/account/account.go +++ b/internal/account/account.go @@ -12,11 +12,10 @@ import ( "strings" "github.com/siliconwitchery/superstack-cli/internal/api" - "github.com/siliconwitchery/superstack-cli/internal/dispatch" ) func Balance(session api.Session, arguments []string) error { - positionals, jsonOutput := dispatch.TakeJsonFlag(arguments) + positionals, jsonOutput := api.TakeJsonFlag(arguments) if len(positionals) > 1 { return errors.New("account balance takes at most one fleet id") diff --git a/internal/api/arguments.go b/internal/api/arguments.go new file mode 100644 index 0000000..7cf8abf --- /dev/null +++ b/internal/api/arguments.go @@ -0,0 +1,17 @@ +package api + +func TakeJsonFlag(arguments []string) ([]string, bool) { + positionals := []string{} + jsonOutput := false + + for _, argument := range arguments { + if argument == "--json" { + jsonOutput = true + continue + } + + positionals = append(positionals, argument) + } + + return positionals, jsonOutput +} diff --git a/internal/api/devices.go b/internal/api/devices.go index 60ebd62..99d4081 100644 --- a/internal/api/devices.go +++ b/internal/api/devices.go @@ -45,17 +45,3 @@ func FetchDevices(session Session) ([]DeviceEntry, error) { return devices, nil } - -func ValidImei(imei string) bool { - if len(imei) != 15 { - return false - } - - for _, digit := range imei { - if digit < '0' || digit > '9' { - return false - } - } - - return true -} diff --git a/internal/device/device.go b/internal/device/device.go index 1edbe68..e09adc9 100644 --- a/internal/device/device.go +++ b/internal/device/device.go @@ -12,7 +12,6 @@ import ( "time" "github.com/siliconwitchery/superstack-cli/internal/api" - "github.com/siliconwitchery/superstack-cli/internal/dispatch" ) func Claim(session api.Session, arguments []string) error { @@ -24,7 +23,7 @@ func Claim(session api.Session, arguments []string) error { imei := arguments[0] - if !api.ValidImei(imei) { + if !validImei(imei) { return errors.New("the IMEI is the 15-digit number printed on the device") } @@ -93,7 +92,7 @@ func Claim(session api.Session, arguments []string) error { } func List(session api.Session, arguments []string) error { - positionals, jsonOutput := dispatch.TakeJsonFlag(arguments) + positionals, jsonOutput := api.TakeJsonFlag(arguments) if len(positionals) > 1 { return errors.New("device list takes at most one fleet id") @@ -262,7 +261,7 @@ func Rename(session api.Session, arguments []string) error { imei := arguments[0] - if !api.ValidImei(imei) { + if !validImei(imei) { return errors.New("the IMEI is the 15-digit number printed on the device") } @@ -310,7 +309,7 @@ func Release(session api.Session, arguments []string) error { imei := arguments[0] - if !api.ValidImei(imei) { + if !validImei(imei) { return errors.New("the IMEI is the 15-digit number printed on the device") } @@ -383,3 +382,17 @@ func Release(session api.Session, arguments []string) error { return nil } + +func validImei(imei string) bool { + if len(imei) != 15 { + return false + } + + for _, digit := range imei { + if digit < '0' || digit > '9' { + return false + } + } + + return true +} diff --git a/internal/dispatch/dispatch.go b/internal/dispatch/dispatch.go index 0c3fcdb..f0218d3 100644 --- a/internal/dispatch/dispatch.go +++ b/internal/dispatch/dispatch.go @@ -9,7 +9,7 @@ import ( "github.com/siliconwitchery/superstack-cli/internal/api" ) -func TakeServerFlag(arguments []string) ([]string, string, error) { +func takeServerFlag(arguments []string) ([]string, string, error) { remaining := []string{} base := api.DefaultBase @@ -17,7 +17,7 @@ func TakeServerFlag(arguments []string) ([]string, string, error) { for index := 0; index < len(arguments); index++ { switch { case arguments[index] == "--server": - if index+1 == len(arguments) || arguments[index+1] == "" { + if index+1 == len(arguments) { return nil, "", errors.New("--server needs an address") } @@ -28,32 +28,18 @@ func TakeServerFlag(arguments []string) ([]string, string, error) { case strings.HasPrefix(arguments[index], "--server="): base = strings.TrimPrefix(arguments[index], "--server=") - if base == "" { - return nil, "", errors.New("--server needs an address") - } - default: remaining = append(remaining, arguments[index]) } } - return remaining, strings.TrimSuffix(base, "/"), nil -} - -func TakeJsonFlag(arguments []string) ([]string, bool) { - positionals := []string{} - jsonOutput := false - - for _, argument := range arguments { - if argument == "--json" { - jsonOutput = true - continue - } + base = strings.TrimRight(base, "/") - positionals = append(positionals, argument) + if base == "" { + return nil, "", errors.New("--server needs an address") } - return positionals, jsonOutput + return remaining, base, nil } type Command struct { @@ -141,7 +127,7 @@ func printHelp(session api.Session, sections []Section) { } func Dispatch(sections []Section, version string, arguments []string, in io.Reader, out io.Writer) error { - arguments, base, err := TakeServerFlag(arguments) + arguments, base, err := takeServerFlag(arguments) if err != nil { return err diff --git a/internal/dispatch/dispatch_test.go b/internal/dispatch/dispatch_test.go index 1829742..237f270 100644 --- a/internal/dispatch/dispatch_test.go +++ b/internal/dispatch/dispatch_test.go @@ -64,11 +64,16 @@ func TestTakeServerFlag(t *testing.T) { arguments: []string{"--server", "", "login"}, wantError: "needs an address", }, + { + name: "an address of nothing but slashes", + arguments: []string{"--server", "//", "login"}, + wantError: "needs an address", + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - remaining, base, err := TakeServerFlag(test.arguments) + remaining, base, err := takeServerFlag(test.arguments) if test.wantError != "" { if err == nil || !strings.Contains(err.Error(), test.wantError) { diff --git a/internal/fleet/fleet.go b/internal/fleet/fleet.go index 8c1f3db..9a9dcdb 100644 --- a/internal/fleet/fleet.go +++ b/internal/fleet/fleet.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/siliconwitchery/superstack-cli/internal/api" - "github.com/siliconwitchery/superstack-cli/internal/dispatch" ) func Create(session api.Session, arguments []string) error { @@ -62,7 +61,7 @@ func Create(session api.Session, arguments []string) error { } func List(session api.Session, arguments []string) error { - positionals, jsonOutput := dispatch.TakeJsonFlag(arguments) + positionals, jsonOutput := api.TakeJsonFlag(arguments) if len(positionals) != 0 { return errors.New("fleet list takes no arguments") diff --git a/internal/key/key.go b/internal/key/key.go index 067a05e..273fc86 100644 --- a/internal/key/key.go +++ b/internal/key/key.go @@ -11,7 +11,6 @@ import ( "strings" "github.com/siliconwitchery/superstack-cli/internal/api" - "github.com/siliconwitchery/superstack-cli/internal/dispatch" ) func Create(session api.Session, arguments []string) error { @@ -69,7 +68,7 @@ func Create(session api.Session, arguments []string) error { } func List(session api.Session, arguments []string) error { - positionals, jsonOutput := dispatch.TakeJsonFlag(arguments) + positionals, jsonOutput := api.TakeJsonFlag(arguments) if len(positionals) > 1 { return errors.New("key list takes at most one fleet id") diff --git a/internal/login/login_test.go b/internal/login/login_test.go index 8c80944..8840533 100644 --- a/internal/login/login_test.go +++ b/internal/login/login_test.go @@ -296,10 +296,6 @@ func TestLogin(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - if testing.Short() && (len(test.pollAnswers) > 0 || strings.Contains(test.deviceAnswer, `"expires_in":0`)) { - t.Skip("the poll loop waits real seconds") - } - apitest.IsolateKeyStorage(t) deviceInterval := test.deviceInterval diff --git a/internal/member/member.go b/internal/member/member.go index 0ef4636..deff734 100644 --- a/internal/member/member.go +++ b/internal/member/member.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/siliconwitchery/superstack-cli/internal/api" - "github.com/siliconwitchery/superstack-cli/internal/dispatch" ) func Add(session api.Session, arguments []string) error { @@ -61,7 +60,7 @@ func Add(session api.Session, arguments []string) error { } func List(session api.Session, arguments []string) error { - positionals, jsonOutput := dispatch.TakeJsonFlag(arguments) + positionals, jsonOutput := api.TakeJsonFlag(arguments) if len(positionals) != 1 { return errors.New("member list takes a fleet id")