From 79d019f9a27dd0fa80e11198feb6394dd22799fe Mon Sep 17 00:00:00 2001 From: mmetc <92726601+mmetc@users.noreply.github.com> Date: Wed, 15 Nov 2023 16:59:30 +0100 Subject: [PATCH] Refact cwhub / sort cscli output, case insensitive (#2593) * dead code: unknown localVersion now defaults to "?" * skip type declaration; whitespace * sync: next item if invalid cpath * func tests for install --force and --ignore * shorter test names * sort cscli output, with tests * cscli: refact hub sort code --- cmd/crowdsec-cli/itemcommands.go | 2 +- cmd/crowdsec-cli/items.go | 41 +++++++++++---------- cmd/crowdsec-cli/utils_table.go | 5 ++- pkg/cwhub/cwhub_test.go | 4 +-- pkg/cwhub/enable.go | 9 +++-- pkg/cwhub/items.go | 10 ++++++ pkg/cwhub/sync.go | 7 ++-- test/bats/20_hub_collections.bats | 48 ++++++++++++++++++++----- test/bats/20_hub_parsers.bats | 52 +++++++++++++++++++++------ test/bats/20_hub_postoverflows.bats | 55 ++++++++++++++++++++++------- test/bats/20_hub_scenarios.bats | 48 ++++++++++++++++++++----- 11 files changed, 205 insertions(+), 76 deletions(-) diff --git a/cmd/crowdsec-cli/itemcommands.go b/cmd/crowdsec-cli/itemcommands.go index 6a8b90996..f93e3b0b9 100644 --- a/cmd/crowdsec-cli/itemcommands.go +++ b/cmd/crowdsec-cli/itemcommands.go @@ -289,7 +289,7 @@ func itemsRemoveRunner(it hubItemType) func(cmd *cobra.Command, args []string) e if all { getter := hub.GetInstalledItems - if (purge) { + if purge { getter = hub.GetAllItems } diff --git a/cmd/crowdsec-cli/items.go b/cmd/crowdsec-cli/items.go index 6d84582db..8104b1ed4 100644 --- a/cmd/crowdsec-cli/items.go +++ b/cmd/crowdsec-cli/items.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "os" - "sort" "strings" "gopkg.in/yaml.v3" @@ -15,7 +14,8 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly bool) ([]string, error) { +// selectItems returns a slice of items of a given type, selected by name and sorted by case-insensitive name +func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly bool) ([]*cwhub.Item, error) { itemNames := hub.GetItemNames(itemType) notExist := []string{} @@ -37,26 +37,32 @@ func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly b installedOnly = false } - if installedOnly { - installed := []string{} - for _, item := range itemNames { - if hub.GetItem(itemType, item).Installed { - installed = append(installed, item) - } + items := make([]*cwhub.Item, 0, len(itemNames)) + + for _, itemName := range itemNames { + item := hub.GetItem(itemType, itemName) + if installedOnly && !item.Installed { + continue } - return installed, nil + + items = append(items, item) } - return itemNames, nil + + cwhub.SortItemSlice(items) + + return items, nil } +// XXX: too complex, should be two functions (itemtypes array and args are not used together) func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string, showType bool, showHeader bool, all bool) error { - items := make(map[string][]string) + items := make(map[string][]*cwhub.Item) + for _, itemType := range itemTypes { selected, err := selectItems(hub, itemType, args, !all) if err != nil { return err } - sort.Strings(selected) + items[itemType] = selected } @@ -79,8 +85,8 @@ func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string, for _, itemType := range itemTypes { // empty slice in case there are no items of this type hubStatus[itemType] = make([]itemHubStatus, len(items[itemType])) - for i, itemName := range items[itemType] { - item := hub.GetItem(itemType, itemName) + + for i, item := range items[itemType] { status, emo := item.Status() hubStatus[itemType][i] = itemHubStatus{ Name: item.Name, @@ -110,13 +116,10 @@ func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string, return fmt.Errorf("failed to write header: %s", err) } } + for _, itemType := range itemTypes { - for _, itemName := range items[itemType] { - item := hub.GetItem(itemType, itemName) + for _, item := range items[itemType] { status, _ := item.Status() - if item.LocalVersion == "" { - item.LocalVersion = "n/a" - } row := []string{ item.Name, status, diff --git a/cmd/crowdsec-cli/utils_table.go b/cmd/crowdsec-cli/utils_table.go index 0336f40a1..67a7a4ec5 100644 --- a/cmd/crowdsec-cli/utils_table.go +++ b/cmd/crowdsec-cli/utils_table.go @@ -11,14 +11,13 @@ import ( "github.com/crowdsecurity/crowdsec/pkg/cwhub" ) -func listHubItemTable(hub *cwhub.Hub, out io.Writer, title string, itemType string, itemNames []string) { +func listHubItemTable(hub *cwhub.Hub, out io.Writer, title string, itemType string, items []*cwhub.Item) { t := newLightTable(out) t.SetHeaders("Name", fmt.Sprintf("%v Status", emoji.Package), "Version", "Local Path") t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) - for itemName := range itemNames { - item := hub.GetItem(itemType, itemNames[itemName]) + for _, item := range items { status, emo := item.Status() t.AddRow(item.Name, fmt.Sprintf("%v %s", emo, status), item.LocalVersion, item.LocalPath) } diff --git a/pkg/cwhub/cwhub_test.go b/pkg/cwhub/cwhub_test.go index af19b93ee..fd95dd2eb 100644 --- a/pkg/cwhub/cwhub_test.go +++ b/pkg/cwhub/cwhub_test.go @@ -60,15 +60,13 @@ func testHub(t *testing.T, update bool) *Hub { os.RemoveAll(tmpDir) }) - var hub *Hub - remote := &RemoteHubCfg{ Branch: "master", URLTemplate: mockURLTemplate, IndexPath: ".index.json", } - hub, err = NewHub(local, remote, update) + hub, err := NewHub(local, remote, update) require.NoError(t, err) return hub diff --git a/pkg/cwhub/enable.go b/pkg/cwhub/enable.go index b2c02b2bb..abe1d5e21 100644 --- a/pkg/cwhub/enable.go +++ b/pkg/cwhub/enable.go @@ -89,6 +89,7 @@ func (i *Item) purge() error { log.Debugf("%s doesn't exist, no need to remove", itempath) return nil } + return fmt.Errorf("while removing file: %w", err) } @@ -101,8 +102,6 @@ func (i *Item) purge() error { // disable removes the symlink to the downloaded content, also removes the content if purge is true func (i *Item) disable(purge bool, force bool) error { // XXX: should return the number of disabled/purged items to inform the upper layer whether to reload or not - var err error - if i.IsLocal() { return fmt.Errorf("%s isn't managed by hub. Please delete manually", i.Name) } @@ -124,7 +123,7 @@ func (i *Item) disable(purge bool, force bool) error { } if removeSub { - if err = sub.disable(purge, force); err != nil { + if err := sub.disable(purge, force); err != nil { return fmt.Errorf("while disabling %s: %w", sub.Name, err) } } else { @@ -170,7 +169,7 @@ func (i *Item) disable(purge bool, force bool) error { return fmt.Errorf("%s isn't managed by hub", i.Name) } - if err = os.Remove(syml); err != nil { + if err := os.Remove(syml); err != nil { if os.IsNotExist(err) { log.Debugf("%s doesn't exist, no need to remove", syml) return nil @@ -184,7 +183,7 @@ func (i *Item) disable(purge bool, force bool) error { i.Installed = false if purge { - if err = i.purge(); err != nil { + if err := i.purge(); err != nil { return err } } diff --git a/pkg/cwhub/items.go b/pkg/cwhub/items.go index 280fcec3e..8e3063a9d 100644 --- a/pkg/cwhub/items.go +++ b/pkg/cwhub/items.go @@ -3,6 +3,8 @@ package cwhub import ( "encoding/json" "fmt" + "sort" + "strings" "github.com/Masterminds/semver/v3" "github.com/enescakir/emoji" @@ -291,6 +293,7 @@ func (h *Hub) GetAllItems(itemType string) ([]*Item, error) { ret := make([]*Item, len(items)) idx := 0 + for _, item := range items { ret[idx] = item idx++ @@ -332,3 +335,10 @@ func (h *Hub) GetInstalledItemsAsString(itemType string) ([]string, error) { return retStr, nil } + +// SortItemSlice sorts a slice of items by name, case insensitive +func SortItemSlice(items []*Item) { + sort.Slice(items, func(i, j int) bool { + return strings.ToLower(items[i].Name) < strings.ToLower(items[j].Name) + }) +} diff --git a/pkg/cwhub/sync.go b/pkg/cwhub/sync.go index fe47342ca..30336fa91 100644 --- a/pkg/cwhub/sync.go +++ b/pkg/cwhub/sync.go @@ -150,10 +150,8 @@ func sortedVersions(raw []string) ([]string, error) { } func (h *Hub) itemVisit(path string, f os.DirEntry, err error) error { - var ( - local bool - hubpath string - ) + local := false + hubpath := "" if err != nil { log.Debugf("while syncing hub dir: %s", err) @@ -398,6 +396,7 @@ func (h *Hub) syncDir(dir string) ([]string, error) { cpath, err := filepath.Abs(fmt.Sprintf("%s/%s", dir, scan)) if err != nil { log.Errorf("failed %s: %s", cpath, err) + continue } // explicit check for non existing directory, avoid spamming log.Debug diff --git a/test/bats/20_hub_collections.bats b/test/bats/20_hub_collections.bats index 9d0f24ba4..f49d0e24b 100644 --- a/test/bats/20_hub_collections.bats +++ b/test/bats/20_hub_collections.bats @@ -79,7 +79,17 @@ teardown() { rune -0 grep -vc 'name,status,version,description' <(output) assert_output "$expected" - # XXX: check alphabetical order in human, json, raw + # the list should be the same in all formats, and sorted (not case sensitive) + + list_raw=$(cscli collections list -o raw -a | tail -n +2 | cut -d, -f1) + list_human=$(cscli collections list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2) + list_json=$(cscli collections list -o json -a | jq -r '.collections[].name') + + rune -0 sort -f <<<"$list_raw" + assert_output "$list_raw" + + assert_equal "$list_raw" "$list_json" + assert_equal "$list_raw" "$list_human" } @test "cscli collections list [collection]..." { @@ -120,7 +130,7 @@ teardown() { assert_output "2" } -@test "cscli collections install [collection]..." { +@test "cscli collections install" { rune -1 cscli collections install assert_stderr --partial 'requires at least 1 arg(s), only received 0' @@ -148,8 +158,7 @@ teardown() { assert_output --partial 'installed: true' } -@test "cscli collections install [collection]... (file location and download-only)" { - # simple install +@test "cscli collections install (file location and download-only)" { rune -0 cscli collections install crowdsecurity/linux --download-only rune -0 cscli collections inspect crowdsecurity/linux --no-metrics assert_output --partial 'crowdsecurity/linux' @@ -158,11 +167,34 @@ teardown() { assert_file_not_exists "$CONFIG_DIR/collections/linux.yaml" rune -0 cscli collections install crowdsecurity/linux + rune -0 cscli collections inspect crowdsecurity/linux --no-metrics + assert_output --partial 'installed: true' assert_file_exists "$CONFIG_DIR/collections/linux.yaml" } +@test "cscli collections install --force (tainted)" { + rune -0 cscli collections install crowdsecurity/sshd + echo "dirty" >"$CONFIG_DIR/collections/sshd.yaml" -@test "cscli collections inspect [collection]..." { + rune -1 cscli collections install crowdsecurity/sshd + assert_stderr --partial "error while installing 'crowdsecurity/sshd': while enabling crowdsecurity/sshd: crowdsecurity/sshd is tainted, won't enable unless --force" + + rune -0 cscli collections install crowdsecurity/sshd --force + assert_stderr --partial "crowdsecurity/sshd: overwrite" + assert_stderr --partial "Enabled crowdsecurity/sshd" +} + +@test "cscli collections install --ignore (skip on errors)" { + rune -1 cscli collections install foo/bar crowdsecurity/sshd + assert_stderr --partial "can't find 'foo/bar' in collections" + refute_stderr --partial "Enabled collections: crowdsecurity/sshd" + + rune -0 cscli collections install foo/bar crowdsecurity/sshd --ignore + assert_stderr --partial "can't find 'foo/bar' in collections" + assert_stderr --partial "Enabled collections: crowdsecurity/sshd" +} + +@test "cscli collections inspect" { rune -1 cscli collections inspect assert_stderr --partial 'requires at least 1 arg(s), only received 0' # required for metrics @@ -223,7 +255,7 @@ teardown() { assert_output "0" } -@test "cscli collections remove [collection]..." { +@test "cscli collections remove" { rune -1 cscli collections remove assert_stderr --partial "specify at least one collection to remove or '--all'" rune -1 cscli collections remove blahblah/blahblah @@ -277,7 +309,7 @@ teardown() { assert_output "0" } -@test "cscli collections remove [collections]... --force" { +@test "cscli collections remove --force" { # remove a collections that belongs to a collection rune -0 cscli collections install crowdsecurity/linux rune -0 cscli collections remove crowdsecurity/sshd @@ -285,7 +317,7 @@ teardown() { assert_stderr --partial "Run 'sudo cscli collections remove crowdsecurity/sshd --force' if you want to force remove this collection" } -@test "cscli collections upgrade [collection]..." { +@test "cscli collections upgrade" { rune -1 cscli collections upgrade assert_stderr --partial "specify at least one collection to upgrade or '--all'" rune -1 cscli collections upgrade blahblah/blahblah diff --git a/test/bats/20_hub_parsers.bats b/test/bats/20_hub_parsers.bats index c17b63315..c780457b3 100644 --- a/test/bats/20_hub_parsers.bats +++ b/test/bats/20_hub_parsers.bats @@ -79,7 +79,17 @@ teardown() { rune -0 grep -vc 'name,status,version,description' <(output) assert_output "$expected" - # XXX: check alphabetical order in human, json, raw + # the list should be the same in all formats, and sorted (not case sensitive) + + list_raw=$(cscli parsers list -o raw -a | tail -n +2 | cut -d, -f1) + list_human=$(cscli parsers list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2) + list_json=$(cscli parsers list -o json -a | jq -r '.parsers[].name') + + rune -0 sort -f <<<"$list_raw" + assert_output "$list_raw" + + assert_equal "$list_raw" "$list_json" + assert_equal "$list_raw" "$list_human" } @test "cscli parsers list [parser]..." { @@ -120,7 +130,7 @@ teardown() { assert_output "3" } -@test "cscli parsers install [parser]..." { +@test "cscli parsers install" { rune -1 cscli parsers install assert_stderr --partial 'requires at least 1 arg(s), only received 0' @@ -148,8 +158,7 @@ teardown() { assert_output --partial 'installed: true' } -@test "cscli parsers install [parser]... (file location and download-only)" { - # simple install +@test "cscli parsers install (file location and download-only)" { rune -0 cscli parsers install crowdsecurity/whitelists --download-only rune -0 cscli parsers inspect crowdsecurity/whitelists --no-metrics assert_output --partial 'crowdsecurity/whitelists' @@ -158,13 +167,34 @@ teardown() { assert_file_not_exists "$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml" rune -0 cscli parsers install crowdsecurity/whitelists + rune -0 cscli parsers inspect crowdsecurity/whitelists --no-metrics + assert_output --partial 'installed: true' assert_file_exists "$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml" } -# XXX: test install with --force -# XXX: test install with --ignore +@test "cscli parsers install --force (tainted)" { + rune -0 cscli parsers install crowdsecurity/whitelists + echo "dirty" >"$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml" -@test "cscli parsers inspect [parser]..." { + rune -1 cscli parsers install crowdsecurity/whitelists + assert_stderr --partial "error while installing 'crowdsecurity/whitelists': while enabling crowdsecurity/whitelists: crowdsecurity/whitelists is tainted, won't enable unless --force" + + rune -0 cscli parsers install crowdsecurity/whitelists --force + assert_stderr --partial "crowdsecurity/whitelists: overwrite" + assert_stderr --partial "Enabled crowdsecurity/whitelists" +} + +@test "cscli parsers install --ignore (skip on errors)" { + rune -1 cscli parsers install foo/bar crowdsecurity/whitelists + assert_stderr --partial "can't find 'foo/bar' in parsers" + refute_stderr --partial "Enabled parsers: crowdsecurity/whitelists" + + rune -0 cscli parsers install foo/bar crowdsecurity/whitelists --ignore + assert_stderr --partial "can't find 'foo/bar' in parsers" + assert_stderr --partial "Enabled parsers: crowdsecurity/whitelists" +} + +@test "cscli parsers inspect" { rune -1 cscli parsers inspect assert_stderr --partial 'requires at least 1 arg(s), only received 0' # required for metrics @@ -195,8 +225,8 @@ teardown() { # one item, raw rune -0 cscli parsers inspect crowdsecurity/sshd-logs -o raw assert_line 'type: parsers' - assert_line 'stage: s01-parse' assert_line 'name: crowdsecurity/sshd-logs' + assert_line 'stage: s01-parse' assert_line 'author: crowdsecurity' assert_line 'remote_path: parsers/s01-parse/crowdsecurity/sshd-logs.yaml' assert_line 'installed: false' @@ -227,7 +257,7 @@ teardown() { assert_output "0" } -@test "cscli parsers remove [parser]..." { +@test "cscli parsers remove" { rune -1 cscli parsers remove assert_stderr --partial "specify at least one parser to remove or '--all'" rune -1 cscli parsers remove blahblah/blahblah @@ -281,7 +311,7 @@ teardown() { assert_output "0" } -@test "cscli parsers remove [parser]... --force" { +@test "cscli parsers remove --force" { # remove a parser that belongs to a collection rune -0 cscli collections install crowdsecurity/sshd rune -0 cscli parsers remove crowdsecurity/sshd-logs @@ -289,7 +319,7 @@ teardown() { assert_stderr --partial "Run 'sudo cscli parsers remove crowdsecurity/sshd-logs --force' if you want to force remove this parser" } -@test "cscli parsers upgrade [parser]..." { +@test "cscli parsers upgrade" { rune -1 cscli parsers upgrade assert_stderr --partial "specify at least one parser to upgrade or '--all'" rune -1 cscli parsers upgrade blahblah/blahblah diff --git a/test/bats/20_hub_postoverflows.bats b/test/bats/20_hub_postoverflows.bats index 23c2ef20b..55c384942 100644 --- a/test/bats/20_hub_postoverflows.bats +++ b/test/bats/20_hub_postoverflows.bats @@ -79,15 +79,24 @@ teardown() { rune -0 grep -vc 'name,status,version,description' <(output) assert_output "$expected" - # XXX: check alphabetical order in human, json, raw + # the list should be the same in all formats, and sorted (not case sensitive) + + list_raw=$(cscli postoverflows list -o raw -a | tail -n +2 | cut -d, -f1) + list_human=$(cscli postoverflows list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2) + list_json=$(cscli postoverflows list -o json -a | jq -r '.postoverflows[].name') + + rune -0 sort -f <<<"$list_raw" + assert_output "$list_raw" + + assert_equal "$list_raw" "$list_json" + assert_equal "$list_raw" "$list_human" } -@test "cscli postoverflows list [scenario]..." { +@test "cscli postoverflows list [postoverflow]..." { # non-existent rune -1 cscli postoverflows install foo/bar assert_stderr --partial "can't find 'foo/bar' in postoverflows" - # not installed rune -0 cscli postoverflows list crowdsecurity/rdns assert_output --regexp 'crowdsecurity/rdns.*disabled' @@ -121,7 +130,7 @@ teardown() { assert_output "3" } -@test "cscli postoverflows install [scenario]..." { +@test "cscli postoverflows install" { rune -1 cscli postoverflows install assert_stderr --partial 'requires at least 1 arg(s), only received 0' @@ -149,8 +158,7 @@ teardown() { assert_output --partial 'installed: true' } -@test "cscli postoverflows install [postoverflow]... (file location and download-only)" { - # simple install +@test "cscli postoverflows install (file location and download-only)" { rune -0 cscli postoverflows install crowdsecurity/rdns --download-only rune -0 cscli postoverflows inspect crowdsecurity/rdns --no-metrics assert_output --partial 'crowdsecurity/rdns' @@ -159,13 +167,34 @@ teardown() { assert_file_not_exists "$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml" rune -0 cscli postoverflows install crowdsecurity/rdns + rune -0 cscli postoverflows inspect crowdsecurity/rdns --no-metrics + assert_output --partial 'installed: true' assert_file_exists "$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml" } -# XXX: test install with --force -# XXX: test install with --ignore +@test "cscli postoverflows install --force (tainted)" { + rune -0 cscli postoverflows install crowdsecurity/rdns + echo "dirty" >"$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml" -@test "cscli postoverflows inspect [scenario]..." { + rune -1 cscli postoverflows install crowdsecurity/rdns + assert_stderr --partial "error while installing 'crowdsecurity/rdns': while enabling crowdsecurity/rdns: crowdsecurity/rdns is tainted, won't enable unless --force" + + rune -0 cscli postoverflows install crowdsecurity/rdns --force + assert_stderr --partial "crowdsecurity/rdns: overwrite" + assert_stderr --partial "Enabled crowdsecurity/rdns" +} + +@test "cscli postoverflow install --ignore (skip on errors)" { + rune -1 cscli postoverflows install foo/bar crowdsecurity/rdns + assert_stderr --partial "can't find 'foo/bar' in postoverflows" + refute_stderr --partial "Enabled postoverflows: crowdsecurity/rdns" + + rune -0 cscli postoverflows install foo/bar crowdsecurity/rdns --ignore + assert_stderr --partial "can't find 'foo/bar' in postoverflows" + assert_stderr --partial "Enabled postoverflows: crowdsecurity/rdns" +} + +@test "cscli postoverflows inspect" { rune -1 cscli postoverflows inspect assert_stderr --partial 'requires at least 1 arg(s), only received 0' # required for metrics @@ -196,8 +225,8 @@ teardown() { # one item, raw rune -0 cscli postoverflows inspect crowdsecurity/rdns -o raw assert_line 'type: postoverflows' - assert_line 'stage: s00-enrich' assert_line 'name: crowdsecurity/rdns' + assert_line 'stage: s00-enrich' assert_line 'author: crowdsecurity' assert_line 'remote_path: postoverflows/s00-enrich/crowdsecurity/rdns.yaml' assert_line 'installed: false' @@ -228,7 +257,7 @@ teardown() { assert_output "0" } -@test "cscli postoverflows remove [postoverflow]..." { +@test "cscli postoverflows remove" { rune -1 cscli postoverflows remove assert_stderr --partial "specify at least one postoverflow to remove or '--all'" rune -1 cscli postoverflows remove blahblah/blahblah @@ -282,7 +311,7 @@ teardown() { assert_output "0" } -@test "cscli postoverflows remove [postoverflow]... --force" { +@test "cscli postoverflows remove --force" { # remove a postoverflow that belongs to a collection rune -0 cscli collections install crowdsecurity/auditd rune -0 cscli postoverflows remove crowdsecurity/auditd-whitelisted-process @@ -290,7 +319,7 @@ teardown() { assert_stderr --partial "Run 'sudo cscli postoverflows remove crowdsecurity/auditd-whitelisted-process --force' if you want to force remove this postoverflow" } -@test "cscli postoverflows upgrade [postoverflow]..." { +@test "cscli postoverflows upgrade" { rune -1 cscli postoverflows upgrade assert_stderr --partial "specify at least one postoverflow to upgrade or '--all'" rune -1 cscli postoverflows upgrade blahblah/blahblah diff --git a/test/bats/20_hub_scenarios.bats b/test/bats/20_hub_scenarios.bats index 8e6d21725..bf033c2f9 100644 --- a/test/bats/20_hub_scenarios.bats +++ b/test/bats/20_hub_scenarios.bats @@ -79,7 +79,17 @@ teardown() { rune -0 grep -vc 'name,status,version,description' <(output) assert_output "$expected" - # XXX: check alphabetical order in human, json, raw + # the list should be the same in all formats, and sorted (not case sensitive) + + list_raw=$(cscli scenarios list -o raw -a | tail -n +2 | cut -d, -f1) + list_human=$(cscli scenarios list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2) + list_json=$(cscli scenarios list -o json -a | jq -r '.scenarios[].name') + + rune -0 sort -f <<<"$list_raw" + assert_output "$list_raw" + + assert_equal "$list_raw" "$list_json" + assert_equal "$list_raw" "$list_human" } @test "cscli scenarios list [scenario]..." { @@ -120,7 +130,7 @@ teardown() { assert_output "3" } -@test "cscli scenarios install [scenario]..." { +@test "cscli scenarios install" { rune -1 cscli scenarios install assert_stderr --partial 'requires at least 1 arg(s), only received 0' @@ -148,7 +158,7 @@ teardown() { assert_output --partial 'installed: true' } -@test "cscli scenarios install [scenario]... (file location and download-only)" { +@test "cscli scenarios install (file location and download-only)" { # simple install rune -0 cscli scenarios install crowdsecurity/ssh-bf --download-only rune -0 cscli scenarios inspect crowdsecurity/ssh-bf --no-metrics @@ -158,14 +168,34 @@ teardown() { assert_file_not_exists "$CONFIG_DIR/scenarios/ssh-bf.yaml" rune -0 cscli scenarios install crowdsecurity/ssh-bf + rune -0 cscli scenarios inspect crowdsecurity/ssh-bf --no-metrics + assert_output --partial 'installed: true' assert_file_exists "$CONFIG_DIR/scenarios/ssh-bf.yaml" } -# XXX: test install with --force -# XXX: test install with --ignore +@test "cscli scenarios install --force (tainted)" { + rune -0 cscli scenarios install crowdsecurity/ssh-bf + echo "dirty" >"$CONFIG_DIR/scenarios/ssh-bf.yaml" + rune -1 cscli scenarios install crowdsecurity/ssh-bf + assert_stderr --partial "error while installing 'crowdsecurity/ssh-bf': while enabling crowdsecurity/ssh-bf: crowdsecurity/ssh-bf is tainted, won't enable unless --force" -@test "cscli scenarios inspect [scenario]..." { + rune -0 cscli scenarios install crowdsecurity/ssh-bf --force + assert_stderr --partial "crowdsecurity/ssh-bf: overwrite" + assert_stderr --partial "Enabled crowdsecurity/ssh-bf" +} + +@test "cscli scenarios install --ignore (skip on errors)" { + rune -1 cscli scenarios install foo/bar crowdsecurity/ssh-bf + assert_stderr --partial "can't find 'foo/bar' in scenarios" + refute_stderr --partial "Enabled scenarios: crowdsecurity/ssh-bf" + + rune -0 cscli scenarios install foo/bar crowdsecurity/ssh-bf --ignore + assert_stderr --partial "can't find 'foo/bar' in scenarios" + assert_stderr --partial "Enabled scenarios: crowdsecurity/ssh-bf" +} + +@test "cscli scenarios inspect" { rune -1 cscli scenarios inspect assert_stderr --partial 'requires at least 1 arg(s), only received 0' # required for metrics @@ -226,7 +256,7 @@ teardown() { assert_output "0" } -@test "cscli scenarios remove [scenario]..." { +@test "cscli scenarios remove" { rune -1 cscli scenarios remove assert_stderr --partial "specify at least one scenario to remove or '--all'" rune -1 cscli scenarios remove blahblah/blahblah @@ -280,7 +310,7 @@ teardown() { assert_output "0" } -@test "cscli scenarios remove [scenario]... --force" { +@test "cscli scenarios remove --force" { # remove a scenario that belongs to a collection rune -0 cscli collections install crowdsecurity/sshd rune -0 cscli scenarios remove crowdsecurity/ssh-bf @@ -288,7 +318,7 @@ teardown() { assert_stderr --partial "Run 'sudo cscli scenarios remove crowdsecurity/ssh-bf --force' if you want to force remove this scenario" } -@test "cscli scenarios upgrade [scenario]..." { +@test "cscli scenarios upgrade" { rune -1 cscli scenarios upgrade assert_stderr --partial "specify at least one scenario to upgrade or '--all'" rune -1 cscli scenarios upgrade blahblah/blahblah