[#11] Add describe flag to tests subcommand

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2025-04-15 15:21:11 +03:00
parent c53f3fa5d0
commit 1440cebda1
4 changed files with 84 additions and 32 deletions

View file

@ -1,27 +1,41 @@
package modules package modules
import ( import (
"cmp"
"fmt" "fmt"
"os" "os"
"slices" "slices"
"sort"
"git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/s3" "git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/s3"
"git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/templates"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var testsCmd = &cobra.Command{ var testsCmd = &cobra.Command{
Use: "tests", Use: "tests",
Short: "Print tests to check", Short: "Print tests info",
Long: "Print all tests that will be checked by 'compatibility' command", Long: "Print all handled tests",
Example: `s3-tests-parser tests Example: `s3-tests-parser tests
s3-tests-parser tests --output tests.txt`, s3-tests-parser tests --output tests.txt`,
RunE: runTestsCmd, RunE: runTestsCmd,
} }
const (
describeFlag = "describe"
)
func initTestsCmd() { func initTestsCmd() {
testsCmd.Flags().String(outputFlag, "", "file to write output, if missed the stdout is used") testsCmd.Flags().String(outputFlag, "", "file to write output, if missed the stdout is used")
testsCmd.Flags().Bool(allFlag, false, "include all tests")
testsCmd.Flags().Bool(describeFlag, false, "describe test (its group, skip status, reason etc.)")
}
type DescribedTest struct {
Name string
Group string
Skip bool
Comment string
} }
func runTestsCmd(cmd *cobra.Command, _ []string) error { func runTestsCmd(cmd *cobra.Command, _ []string) error {
@ -30,34 +44,40 @@ func runTestsCmd(cmd *cobra.Command, _ []string) error {
return err return err
} }
var tests []string includeAll := viper.GetBool(allFlag)
groupTests := make(map[string][]string) var tests []DescribedTest
for _, group := range testStruct.Groups {
if group.Skip {
continue
}
groupTests[group.Name] = group.Tests
}
for _, group := range testStruct.Groups { for _, group := range testStruct.Groups {
if group.Skip { if group.Skip && !includeAll {
continue continue
} }
tests = append(tests, group.Tests...) for _, test := range group.Tests {
for _, include := range group.Include { tests = append(tests, DescribedTest{
tests = append(tests, groupTests[include]...) Name: test,
Group: group.Name,
Skip: group.Skip,
Comment: group.Comment,
})
} }
} }
sort.Strings(tests) slices.SortFunc(tests, func(a, b DescribedTest) int {
tests = slices.Compact(tests) return cmp.Compare(a.Name, b.Name)
})
tests = slices.CompactFunc(tests, func(a DescribedTest, b DescribedTest) bool {
return a.Name == b.Name
})
if viper.GetBool(describeFlag) {
return describeTests(cmd, tests)
}
return printTests(cmd, tests) return printTests(cmd, tests)
} }
func printTests(cmd *cobra.Command, res []string) error { func printTests(cmd *cobra.Command, res []DescribedTest) error {
w := cmd.OutOrStdout() w := cmd.OutOrStdout()
if outFile := viper.GetString(outputFlag); outFile != "" { if outFile := viper.GetString(outputFlag); outFile != "" {
f, err := os.Create(outFile) f, err := os.Create(outFile)
@ -69,10 +89,29 @@ func printTests(cmd *cobra.Command, res []string) error {
} }
for i := range res { for i := range res {
if _, err := fmt.Fprintln(w, res[i]); err != nil { if _, err := fmt.Fprintln(w, res[i].Name); err != nil {
return err return err
} }
} }
return nil return nil
} }
func describeTests(cmd *cobra.Command, res []DescribedTest) error {
w := cmd.OutOrStdout()
if outFile := viper.GetString(outputFlag); outFile != "" {
f, err := os.Create(outFile)
if err != nil {
return fmt.Errorf("create out file: %w", err)
}
w = f
defer f.Close()
}
outTemplate, err := templates.GetDescribeTemplate()
if err != nil {
return fmt.Errorf("form describe template: %w", err)
}
return outTemplate.Execute(w, res)
}

View file

@ -342,7 +342,7 @@
"tests": [], "tests": [],
"include": [], "include": [],
"skip": true, "skip": true,
"comment": "Not supported. This method is deprecated by AWS. Use GetBucketLifecycleConfiguration" "comment": "Not supported <br>This method is deprecated by AWS, use GetBucketLifecycleConfiguration"
}, },
{ {
"name": "GetBucketLifecycleConfiguration", "name": "GetBucketLifecycleConfiguration",
@ -829,7 +829,7 @@
"tests": [], "tests": [],
"include": [], "include": [],
"skip": true, "skip": true,
"comment": "Not supported. This method is deprecated by AWS. Use PutBucketLifecycleConfiguration" "comment": "Not supported <br>This method is deprecated by AWS, use PutBucketLifecycleConfiguration"
}, },
{ {
"name": "PutBucketLifecycleConfiguration", "name": "PutBucketLifecycleConfiguration",
@ -1219,7 +1219,7 @@
"s3tests_boto3/functional/test_s3.py::test_bucket_concurrent_set_canned_acl" "s3tests_boto3/functional/test_s3.py::test_bucket_concurrent_set_canned_acl"
], ],
"skip": false, "skip": false,
"comment": "Partly supported. Only restricted canned acl can be applied" "comment": "Partly supported <br>Only restricted canned acl can be applied"
}, },
{ {
"name": "BucketVersioning", "name": "BucketVersioning",
@ -1249,7 +1249,7 @@
"s3tests_boto3/functional/test_s3.py::test_versioning_concurrent_multi_object_delete" "s3tests_boto3/functional/test_s3.py::test_versioning_concurrent_multi_object_delete"
], ],
"skip": false, "skip": false,
"comment": "Limitations: \n1. Don't create delete marker for non-existing objects" "comment": "Limitations: <br>1. Don't create delete marker for non-existing objects"
}, },
{ {
"name": "MultipartUpload", "name": "MultipartUpload",
@ -1379,7 +1379,7 @@
"s3tests_boto3/functional/test_s3.py::test_post_object_wrong_bucket" "s3tests_boto3/functional/test_s3.py::test_post_object_wrong_bucket"
], ],
"skip": true, "skip": true,
"comment": "Not supported. AWS v2 signature is deprecated." "comment": "Not supported <br>AWS v2 signature is deprecated"
}, },
{ {
"name": "Locking", "name": "Locking",
@ -1526,7 +1526,7 @@
"s3tests_boto3/functional/test_s3.py::test_bucket_acl_default" "s3tests_boto3/functional/test_s3.py::test_bucket_acl_default"
], ],
"skip": true, "skip": true,
"comment": "Not supported, Full Control to bucket owner is required" "comment": "Not supported <br>Full Control to bucket owner is required"
}, },
{ {
"name": "Bucket ACL, using payload", "name": "Bucket ACL, using payload",
@ -1541,7 +1541,7 @@
"s3tests_boto3/functional/test_s3.py::test_bucket_header_acl_grants" "s3tests_boto3/functional/test_s3.py::test_bucket_header_acl_grants"
], ],
"skip": true, "skip": true,
"comment": "Not supported, Full Control to bucket owner is required" "comment": "Not supported <br>Full Control to bucket owner is required"
}, },
{ {
"name": "Email checking", "name": "Email checking",
@ -1551,7 +1551,7 @@
"s3tests_boto3/functional/test_s3.py::test_bucket_acl_grant_email_not_exist" "s3tests_boto3/functional/test_s3.py::test_bucket_acl_grant_email_not_exist"
], ],
"skip": true, "skip": true,
"comment": "Not supported, email checking" "comment": "Not supported <br>Email checking"
}, },
{ {
"name": "Authorization header manipulating", "name": "Authorization header manipulating",
@ -1561,7 +1561,7 @@
"s3tests_boto3/functional/test_headers.py::test_bucket_create_bad_authorization_none" "s3tests_boto3/functional/test_headers.py::test_bucket_create_bad_authorization_none"
], ],
"skip": true, "skip": true,
"comment": "Bad test, railed_on_rwg, # TODO: remove 'fails_on_rgw' and once we have learned how to manipulate the authorization header" "comment": "Bad test <br>failed_on_rwg <br># TODO: remove 'fails_on_rgw' and once we have learned how to manipulate the authorization header"
}, },
{ {
"name": "test_bucket_create_bad_expect_mismatch", "name": "test_bucket_create_bad_expect_mismatch",
@ -1579,7 +1579,7 @@
"s3tests_boto3/functional/test_s3.py::test_bucket_create_exists" "s3tests_boto3/functional/test_s3.py::test_bucket_create_exists"
], ],
"skip": true, "skip": true,
"comment": "Bad test. Incorrect asserting response error" "comment": "Bad test <br>Incorrect asserting response error"
}, },
{ {
"name": "Checking object owner", "name": "Checking object owner",
@ -1669,7 +1669,7 @@
"s3tests_boto3/functional/test_s3.py::test_sse_s3_encrypted_upload_8mb" "s3tests_boto3/functional/test_s3.py::test_sse_s3_encrypted_upload_8mb"
], ],
"skip": true, "skip": true,
"comment": "sse_s3 not supported" "comment": "SSE-S3 not supported"
}, },
{ {
"name": "alternative client in different account", "name": "alternative client in different account",
@ -1716,7 +1716,7 @@
"s3tests_boto3/functional/test_s3.py::test_lifecycle_expiration_header_tags_head" "s3tests_boto3/functional/test_s3.py::test_lifecycle_expiration_header_tags_head"
], ],
"skip": true, "skip": true,
"comment": "Probably bad test, AWS fails, https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/issues/545" "comment": "Probably bad test <br>AWS fails <br>https://git.frostfs.info/TrueCloudLab/frostfs-s3-gw/issues/545"
}, },
{ {
"name": "get bucket policy status", "name": "get bucket policy status",
@ -1729,7 +1729,7 @@
"s3tests_boto3/functional/test_s3.py::test_get_nonpublicpolicy_acl_bucket_policy_status" "s3tests_boto3/functional/test_s3.py::test_get_nonpublicpolicy_acl_bucket_policy_status"
], ],
"skip": true, "skip": true,
"comment": "Not supported, S3 gate computes status based on policy, not on canned acl. AWS also fails" "comment": "Not supported <br>S3 gate computes status based on policy, not on canned acl <br>AWS also fails"
}, },
{ {
"name": "Lifecycle transition", "name": "Lifecycle transition",

View file

@ -0,0 +1,7 @@
# S3 Tests
Commit: [7aae6ce2ca48ccd0ac35100af074281c7c88a210](https://git.frostfs.info/TrueCloudLab/s3-tests/commit/7aae6ce2ca48ccd0ac35100af074281c7c88a210)
| Test | Group | Skip | Comment |
|------|-------|------|---------|{{range .}}
| {{.Name}} | {{.Group}} | {{.Skip}} | {{.Comment}} |{{end}}

View file

@ -35,6 +35,8 @@ var (
txtTemplate []byte txtTemplate []byte
//go:embed resources/md-template.gotmpl //go:embed resources/md-template.gotmpl
mdTemplate []byte mdTemplate []byte
//go:embed resources/tests-describe-template.gotmpl
describeTemplate []byte
) )
func GetTemplate(outFormat string) (*template.Template, error) { func GetTemplate(outFormat string) (*template.Template, error) {
@ -84,3 +86,7 @@ func ColorToTerminal(color string) string {
return blackTerminal return blackTerminal
} }
} }
func GetDescribeTemplate() (*template.Template, error) {
return template.New("describe-tests").Parse(string(describeTemplate))
}