forked from TrueCloudLab/s3-tests-parser
Support result formatting
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
8ae5fa912b
commit
ca943e1dc8
8 changed files with 456 additions and 225 deletions
|
@ -19,5 +19,5 @@ $ ./bin/s3-tests-parser compatibility tests/fixture/suite.csv
|
|||
You can also use json
|
||||
|
||||
```shell
|
||||
$ ./bin/s3-tests-parser compatibility tests/fixture/suite.json --format json
|
||||
$ ./bin/s3-tests-parser compatibility tests/fixture/suite.json --format json --output-format md --output result.md
|
||||
```
|
||||
|
|
|
@ -1,56 +1,65 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/s3-tests-parser/internal/parser"
|
||||
"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/viper"
|
||||
)
|
||||
|
||||
//go:embed resources/tests-struct.json
|
||||
var testStructData []byte
|
||||
|
||||
var compatibilityCmd = &cobra.Command{
|
||||
Use: "compatibility",
|
||||
Short: "Shows compatibility results",
|
||||
Long: "Form compatibility table based on passed s3 tests",
|
||||
Example: `s3-tests-parser compatibility suite.csv
|
||||
s3-tests-parser compatibility suite.json --format json`,
|
||||
s3-tests-parser compatibility suite.json --format json
|
||||
s3-tests-parser compatibility suite.json --format json --output-format md
|
||||
s3-tests-parser compatibility suite.json --format json --output-format md --output result.md`,
|
||||
RunE: runCompatibilityCmd,
|
||||
}
|
||||
|
||||
type (
|
||||
TestsStructure struct {
|
||||
Groups []Group `json:"groups"`
|
||||
Results struct {
|
||||
Legend []Status
|
||||
TagGroups []TagGroup
|
||||
}
|
||||
|
||||
Group struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
Skip bool `json:"skip"`
|
||||
SkipReason string `json:"skip_reason"`
|
||||
Tests []string `json:"tests"`
|
||||
TagGroup struct {
|
||||
Name string
|
||||
Tests []TestResult
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
Reset = "\033[0m"
|
||||
Red = "\033[31m"
|
||||
Green = "\033[32m"
|
||||
Yellow = "\033[33m"
|
||||
Status struct {
|
||||
Color string
|
||||
Description string
|
||||
}
|
||||
|
||||
TestResult struct {
|
||||
Color string
|
||||
Name string
|
||||
Comment string
|
||||
Passed int
|
||||
Total int
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
formatFlag = "format"
|
||||
outputFlag = "output"
|
||||
outputFormatFlag = "output-format"
|
||||
)
|
||||
|
||||
func initCompatibilityCmd() {
|
||||
compatibilityCmd.Flags().String(formatFlag, "csv", "format of input test suite file")
|
||||
compatibilityCmd.Flags().String(outputFlag, "", "file to write output, if missed the stdout is used")
|
||||
compatibilityCmd.Flags().String(outputFormatFlag, "txt", "format of output")
|
||||
}
|
||||
|
||||
func runCompatibilityCmd(cmd *cobra.Command, args []string) error {
|
||||
|
@ -58,31 +67,70 @@ func runCompatibilityCmd(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("expected exactly one arg, got: %v", args)
|
||||
}
|
||||
|
||||
var testStruct TestsStructure
|
||||
if err := json.Unmarshal(testStructData, &testStruct); err != nil {
|
||||
return fmt.Errorf("failed to parse tests struct: %w", err)
|
||||
testStruct, err := s3.ParseTestsStruct()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
testsMap, err := parseSuite(args[0], viper.GetString(formatFlag))
|
||||
testsMap, err := parser.ParseSuite(args[0], viper.GetString(formatFlag))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse tests: %w", err)
|
||||
}
|
||||
|
||||
for _, group := range testStruct.Groups {
|
||||
if group.Skip {
|
||||
cmd.Println(fmt.Sprintf("%s; skip: %s", group.Name, group.SkipReason))
|
||||
continue
|
||||
}
|
||||
|
||||
printResult(cmd, group, testsMap)
|
||||
}
|
||||
|
||||
return nil
|
||||
res := formResults(testStruct, testsMap)
|
||||
return printResults(cmd, res)
|
||||
}
|
||||
|
||||
func printResult(cmd *cobra.Command, group Group, testsMap map[string]bool) {
|
||||
ln := float64(len(group.Tests))
|
||||
pass := 0.0
|
||||
var legend = []Status{
|
||||
{
|
||||
Color: templates.GreenColor,
|
||||
Description: "Supported",
|
||||
},
|
||||
{
|
||||
Color: templates.YellowColor,
|
||||
Description: "Partially supported",
|
||||
},
|
||||
{
|
||||
Color: templates.RedColor,
|
||||
Description: "Badly supported",
|
||||
},
|
||||
{
|
||||
Color: templates.BlueColor,
|
||||
Description: "Not supported yet, but will be in future",
|
||||
},
|
||||
{
|
||||
Color: templates.BlackColor,
|
||||
Description: "Not applicable or will never be supported",
|
||||
},
|
||||
}
|
||||
|
||||
func formResults(testStruct s3.TestsStructure, testsMap map[string]bool) Results {
|
||||
tagGroups := make(map[string]TagGroup)
|
||||
for _, group := range testStruct.Groups {
|
||||
tagGroup, ok := tagGroups[group.Tag]
|
||||
if !ok {
|
||||
tagGroup.Name = group.Tag
|
||||
}
|
||||
|
||||
tagGroup.Tests = append(tagGroup.Tests, formTestResult(group, testsMap))
|
||||
tagGroups[group.Tag] = tagGroup
|
||||
}
|
||||
|
||||
res := Results{Legend: legend}
|
||||
for _, group := range tagGroups {
|
||||
res.TagGroups = append(res.TagGroups, group)
|
||||
}
|
||||
|
||||
sort.Slice(res.TagGroups, func(i, j int) bool {
|
||||
return res.TagGroups[i].Name < res.TagGroups[j].Name
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func formTestResult(group s3.Group, testsMap map[string]bool) TestResult {
|
||||
ln := len(group.Tests)
|
||||
pass := 0
|
||||
|
||||
for _, test := range group.Tests {
|
||||
if testsMap[test] {
|
||||
|
@ -90,95 +138,47 @@ func printResult(cmd *cobra.Command, group Group, testsMap map[string]bool) {
|
|||
}
|
||||
}
|
||||
|
||||
color := Red
|
||||
rate := pass / ln
|
||||
var color string
|
||||
if strings.Contains(group.Comment, "Not supported yet") {
|
||||
color = templates.BlueColor
|
||||
} else if strings.Contains(group.Comment, "Not applicable") {
|
||||
color = templates.BlackColor
|
||||
}
|
||||
|
||||
if color == "" {
|
||||
color = templates.RedColor
|
||||
rate := float64(pass) / float64(ln)
|
||||
if rate > 0.9 {
|
||||
color = Green
|
||||
color = templates.GreenColor
|
||||
} else if rate > 0.5 {
|
||||
color = Yellow
|
||||
color = templates.YellowColor
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Println(fmt.Sprintf("%s%s: %d/%d \u001B[0m", color, group.Name, int(pass), int(ln)))
|
||||
}
|
||||
|
||||
func parseSuite(filePath string, format string) (map[string]bool, error) {
|
||||
switch format {
|
||||
case "csv":
|
||||
return parseSuiteCSV(filePath)
|
||||
case "json":
|
||||
return parseSuiteJSON(filePath)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format: %s", format)
|
||||
return TestResult{
|
||||
Color: color,
|
||||
Name: group.Name,
|
||||
Comment: group.Comment,
|
||||
Passed: pass,
|
||||
Total: ln,
|
||||
}
|
||||
}
|
||||
|
||||
func parseSuiteCSV(filePath string) (map[string]bool, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
func printResults(cmd *cobra.Command, res Results) error {
|
||||
w := cmd.OutOrStdout()
|
||||
if outFile := viper.GetString(outputFlag); outFile != "" {
|
||||
f, err := os.Create(outFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read suite file: %w", err)
|
||||
return fmt.Errorf("create out file: %w", err)
|
||||
}
|
||||
w = f
|
||||
defer f.Close()
|
||||
}
|
||||
|
||||
rr := csv.NewReader(bytes.NewReader(data))
|
||||
records, err := rr.ReadAll()
|
||||
outTemplate, err := templates.GetTemplate(viper.GetString(outputFormatFlag))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse suite file: %w", err)
|
||||
return fmt.Errorf("form out template: %w", err)
|
||||
}
|
||||
|
||||
indexName := -1
|
||||
indexStatus := -1
|
||||
tests := make(map[string]bool)
|
||||
|
||||
for i, recs := range records {
|
||||
if i == 0 {
|
||||
for j, rec := range recs {
|
||||
if rec == "Name" {
|
||||
indexName = j
|
||||
} else if rec == "Status" {
|
||||
indexStatus = j
|
||||
}
|
||||
}
|
||||
|
||||
if indexName == -1 || indexStatus == -1 {
|
||||
return nil, fmt.Errorf("invalid csv format, couldn't find 'Name' and 'Status' fields")
|
||||
}
|
||||
}
|
||||
|
||||
tests[recs[indexName]] = recs[indexStatus] == "passed"
|
||||
}
|
||||
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
type SuiteNode struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Children []SuiteNode `json:"children"`
|
||||
}
|
||||
|
||||
func parseSuiteJSON(filePath string) (map[string]bool, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read suite file: %w", err)
|
||||
}
|
||||
|
||||
var suiteNode SuiteNode
|
||||
if err = json.Unmarshal(data, &suiteNode); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse suite file: %w", err)
|
||||
}
|
||||
|
||||
tests := make(map[string]bool)
|
||||
parseSuiteNode(suiteNode, tests)
|
||||
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
func parseSuiteNode(node SuiteNode, res map[string]bool) {
|
||||
if node.Status != "" {
|
||||
res[node.Name] = node.Status == "passed"
|
||||
return
|
||||
}
|
||||
|
||||
for _, child := range node.Children {
|
||||
parseSuiteNode(child, res)
|
||||
}
|
||||
return outTemplate.Execute(w, res)
|
||||
}
|
||||
|
|
91
internal/parser/parser.go
Normal file
91
internal/parser/parser.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func ParseSuite(filePath string, format string) (map[string]bool, error) {
|
||||
switch format {
|
||||
case "csv":
|
||||
return parseSuiteCSV(filePath)
|
||||
case "json":
|
||||
return parseSuiteJSON(filePath)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format: %s", format)
|
||||
}
|
||||
}
|
||||
|
||||
func parseSuiteCSV(filePath string) (map[string]bool, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read suite file: %w", err)
|
||||
}
|
||||
|
||||
rr := csv.NewReader(bytes.NewReader(data))
|
||||
records, err := rr.ReadAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse suite file: %w", err)
|
||||
}
|
||||
|
||||
indexName := -1
|
||||
indexStatus := -1
|
||||
tests := make(map[string]bool)
|
||||
|
||||
for i, recs := range records {
|
||||
if i == 0 {
|
||||
for j, rec := range recs {
|
||||
if rec == "Name" {
|
||||
indexName = j
|
||||
} else if rec == "Status" {
|
||||
indexStatus = j
|
||||
}
|
||||
}
|
||||
|
||||
if indexName == -1 || indexStatus == -1 {
|
||||
return nil, fmt.Errorf("invalid csv format, couldn't find 'Name' and 'Status' fields")
|
||||
}
|
||||
}
|
||||
|
||||
tests[recs[indexName]] = recs[indexStatus] == "passed"
|
||||
}
|
||||
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
type suiteNode struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Children []suiteNode `json:"children"`
|
||||
}
|
||||
|
||||
func parseSuiteJSON(filePath string) (map[string]bool, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read suite file: %w", err)
|
||||
}
|
||||
|
||||
var suiteNode suiteNode
|
||||
if err = json.Unmarshal(data, &suiteNode); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse suite file: %w", err)
|
||||
}
|
||||
|
||||
tests := make(map[string]bool)
|
||||
parseSuiteNode(suiteNode, tests)
|
||||
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
func parseSuiteNode(node suiteNode, res map[string]bool) {
|
||||
if node.Status != "" {
|
||||
res[node.Name] = node.Status == "passed"
|
||||
return
|
||||
}
|
||||
|
||||
for _, child := range node.Children {
|
||||
parseSuiteNode(child, res)
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
"test_list_multipart_upload_owner"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "CompleteMultipartUpload",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"test_multipart_copy_multiple_sizes"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "CopyObject",
|
||||
|
@ -72,7 +72,7 @@
|
|||
"test_object_copy_key_not_found"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "CreateBucket",
|
||||
|
@ -130,7 +130,7 @@
|
|||
"test_buckets_list_ctime"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "CreateMultipartUpload",
|
||||
|
@ -175,7 +175,7 @@
|
|||
"test_sse_s3_default_multipart_upload"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucket",
|
||||
|
@ -190,14 +190,14 @@
|
|||
"test_atomic_write_bucket_gone"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketAnalyticsConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketCors",
|
||||
|
@ -206,7 +206,7 @@
|
|||
"test_set_cors"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketEncryption",
|
||||
|
@ -216,42 +216,42 @@
|
|||
"test_delete_bucket_encryption_kms"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketIntelligentTieringConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketInventoryConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketLifecycle",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketMetricsConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketOwnershipControls",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketPolicy",
|
||||
|
@ -261,14 +261,14 @@
|
|||
"test_bucketv2_policy_acl"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketReplication",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketTagging",
|
||||
|
@ -277,14 +277,14 @@
|
|||
"test_set_bucket_tagging"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeleteBucketWebsite",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "DeleteObject",
|
||||
|
@ -326,7 +326,7 @@
|
|||
"test_object_lock_uploading_obj"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeleteObjects",
|
||||
|
@ -344,7 +344,7 @@
|
|||
"test_versioning_concurrent_multi_object_delete"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeleteObjectTagging",
|
||||
|
@ -354,21 +354,21 @@
|
|||
"test_delete_tags_obj_public"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "DeletePublicAccessBlock",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketAccelerateConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not applicable or will never be supported"
|
||||
"comment": "Not applicable or will never be supported"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketAcl",
|
||||
|
@ -395,14 +395,14 @@
|
|||
"test_bucket_acl_grant_userid_writeacp"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetBucketAnalyticsConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketCors",
|
||||
|
@ -414,7 +414,7 @@
|
|||
"test_cors_header_option"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetBucketEncryption",
|
||||
|
@ -424,28 +424,28 @@
|
|||
"test_get_bucket_encryption_kms"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketIntelligentTieringConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketInventoryConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketLifecycle",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketLifecycleConfiguration",
|
||||
|
@ -455,7 +455,7 @@
|
|||
"test_lifecycle_get_no_id"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketLocation",
|
||||
|
@ -464,7 +464,7 @@
|
|||
"test_bucket_get_location"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetBucketLogging",
|
||||
|
@ -473,35 +473,35 @@
|
|||
"test_logging_toggle"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketMetricsConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketNotification",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketNotificationConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketOwnershipControls",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketPolicy",
|
||||
|
@ -512,7 +512,7 @@
|
|||
"test_bucket_policy_set_condition_operator_end_with_IfExists"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetBucketPolicyStatus",
|
||||
|
@ -526,21 +526,21 @@
|
|||
"test_get_nonpublicpolicy_deny_bucket_policy_status"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketReplication",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketRequestPayment",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not applicable or will never be supported"
|
||||
"comment": "Not applicable or will never be supported"
|
||||
},
|
||||
{
|
||||
"name": "GetBucketTagging",
|
||||
|
@ -549,7 +549,7 @@
|
|||
"test_set_bucket_tagging"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetBucketVersioning",
|
||||
|
@ -563,14 +563,14 @@
|
|||
"test_versioning_obj_plain_null_version_overwrite_suspended"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetBucketWebsite",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "GetObject",
|
||||
|
@ -634,7 +634,7 @@
|
|||
"test_bucket_create_special_key_names"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectAcl",
|
||||
|
@ -664,14 +664,14 @@
|
|||
"test_bucket_policy_get_obj_acl_existing_tag"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectAttributes",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectLegalHold",
|
||||
|
@ -681,7 +681,7 @@
|
|||
"test_object_lock_get_legal_hold_invalid_bucket"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectLockConfiguration",
|
||||
|
@ -691,7 +691,7 @@
|
|||
"test_object_lock_get_obj_lock_invalid_bucket"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectRetention",
|
||||
|
@ -705,7 +705,7 @@
|
|||
"test_object_lock_put_obj_retention_shorten_period_bypass"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectTagging",
|
||||
|
@ -728,7 +728,7 @@
|
|||
"test_bucket_policy_get_obj_acl_existing_tag"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "GetObjectTorrent",
|
||||
|
@ -737,7 +737,7 @@
|
|||
"test_get_object_torrent"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not applicable or will never be supported"
|
||||
"comment": "Not applicable or will never be supported"
|
||||
},
|
||||
{
|
||||
"name": "GetPublicAccessBlock",
|
||||
|
@ -746,7 +746,7 @@
|
|||
"test_get_default_public_block"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "HeadBucket",
|
||||
|
@ -758,7 +758,7 @@
|
|||
"test_bucket_head_extended"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "HeadObject",
|
||||
|
@ -782,35 +782,35 @@
|
|||
"test_sse_s3_default_method_head"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "ListBucketAnalyticsConfigurations",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "ListBucketIntelligentTieringConfigurations",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "ListBucketInventoryConfigurations",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "ListBucketMetricsConfigurations",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "ListBuckets",
|
||||
|
@ -824,7 +824,7 @@
|
|||
"test_list_buckets_bad_auth"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "ListMultipartUploads",
|
||||
|
@ -835,7 +835,7 @@
|
|||
"test_lifecycle_multipart_expiration"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "ListObjects",
|
||||
|
@ -896,7 +896,7 @@
|
|||
"test_bucket_list_delimiter_prefix_underscore"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "ListObjectsV2",
|
||||
|
@ -963,7 +963,7 @@
|
|||
"test_sse_s3_default_multipart_upload"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "ListObjectVersions",
|
||||
|
@ -992,21 +992,21 @@
|
|||
"test_versioning_bucket_multipart_upload_return_version_id"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "ListParts",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutBucketAccelerateConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not applicable or will never be supported"
|
||||
"comment": "Not applicable or will never be supported"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketAcl",
|
||||
|
@ -1052,14 +1052,14 @@
|
|||
"test_access_bucket_publicreadwrite_object_publicreadwrite"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutBucketAnalyticsConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketCors",
|
||||
|
@ -1073,7 +1073,7 @@
|
|||
"test_cors_presigned_get_object_tenant"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutBucketEncryption",
|
||||
|
@ -1095,28 +1095,28 @@
|
|||
"test_sse_kms_default_upload_8mb"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketIntelligentTieringConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketInventoryConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketLifecycle",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketLifecycleConfiguration",
|
||||
|
@ -1160,7 +1160,7 @@
|
|||
"test_lifecycle_cloud_transition_large_obj"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketLogging",
|
||||
|
@ -1169,35 +1169,35 @@
|
|||
"test_logging_toggle"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketMetricsConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketNotification",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketNotificationConfiguration",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketOwnershipControls",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketPolicy",
|
||||
|
@ -1226,21 +1226,21 @@
|
|||
"test_multipart_upload_on_a_bucket_with_policy"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutBucketReplication",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketRequestPayment",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not applicable or will never be supported"
|
||||
"comment": "Not applicable or will never be supported"
|
||||
},
|
||||
{
|
||||
"name": "PutBucketTagging",
|
||||
|
@ -1249,7 +1249,7 @@
|
|||
"test_set_bucket_tagging"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutBucketVersioning",
|
||||
|
@ -1281,14 +1281,14 @@
|
|||
"test_versioning_bucket_multipart_upload_return_version_id"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutBucketWebsite",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "PutObject",
|
||||
|
@ -1340,7 +1340,7 @@
|
|||
"test_object_acl_canned"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutObjectAcl",
|
||||
|
@ -1372,7 +1372,7 @@
|
|||
"test_access_bucket_publicreadwrite_object_publicreadwrite"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutObjectLegalHold",
|
||||
|
@ -1388,7 +1388,7 @@
|
|||
"test_object_lock_uploading_obj"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutObjectLockConfiguration",
|
||||
|
@ -1405,7 +1405,7 @@
|
|||
"test_object_lock_put_obj_retention_override_default_retention"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutObjectRetention",
|
||||
|
@ -1430,7 +1430,7 @@
|
|||
"test_object_lock_changing_mode_from_compliance"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutObjectTagging",
|
||||
|
@ -1460,7 +1460,7 @@
|
|||
"test_bucket_policy_get_obj_acl_existing_tag"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "PutPublicAccessBlock",
|
||||
|
@ -1473,14 +1473,14 @@
|
|||
"test_ignore_public_acls"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "RestoreObject",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "SelectObjectContent",
|
||||
|
@ -1522,7 +1522,7 @@
|
|||
"test_output_serial_expressions"
|
||||
],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "UploadPart",
|
||||
|
@ -1550,7 +1550,7 @@
|
|||
"test_list_multipart_upload_owner"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "UploadPartCopy",
|
||||
|
@ -1565,18 +1565,18 @@
|
|||
"test_multipart_copy_multiple_sizes"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
},
|
||||
{
|
||||
"name": "WriteGetObjectResponse",
|
||||
"tag": "API",
|
||||
"tests": [],
|
||||
"skip": true,
|
||||
"skip_reason": "Not supported yet, but will be in future"
|
||||
"comment": "Not supported yet, but will be in future"
|
||||
},
|
||||
{
|
||||
"name": "encryption",
|
||||
"tag": "feature",
|
||||
"tag": "Features",
|
||||
"tests": [
|
||||
"test_sse_kms_method_head",
|
||||
"test_sse_kms_present",
|
||||
|
@ -1626,11 +1626,11 @@
|
|||
"test_encryption_sse_c_post_object_authenticated_request"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": "Only sse-c currently supported"
|
||||
},
|
||||
{
|
||||
"name": "post_upload",
|
||||
"tag": "feature",
|
||||
"tag": "Features",
|
||||
"tests": [
|
||||
"test_post_object_anonymous_request",
|
||||
"test_post_object_authenticated_request",
|
||||
|
@ -1671,11 +1671,11 @@
|
|||
"test_sse_kms_post_object_authenticated_request"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": "Supported only basic POST upload"
|
||||
},
|
||||
{
|
||||
"name": "aws_v2_signature",
|
||||
"tag": "feature",
|
||||
"tag": "Features",
|
||||
"tests": [
|
||||
"test_object_create_bad_md5_invalid_garbage_aws2",
|
||||
"test_object_create_bad_contentlength_mismatch_below_aws2",
|
||||
|
@ -1691,11 +1691,11 @@
|
|||
"test_object_create_bad_date_after_end_aws2"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": "AWS v2 signature is deprecated"
|
||||
},
|
||||
{
|
||||
"name": "locking",
|
||||
"tag": "feature",
|
||||
"tag": "Features",
|
||||
"tests": [
|
||||
"test_object_lock_put_obj_lock",
|
||||
"test_object_lock_put_obj_lock_invalid_bucket",
|
||||
|
@ -1728,7 +1728,7 @@
|
|||
"test_object_lock_get_obj_metadata"
|
||||
],
|
||||
"skip": false,
|
||||
"skip_reason": ""
|
||||
"comment": ""
|
||||
}
|
||||
]
|
||||
}
|
31
internal/s3/structure.go
Normal file
31
internal/s3/structure.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package s3
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//go:embed resources/tests-struct.json
|
||||
var testStructData []byte
|
||||
|
||||
type TestsStructure struct {
|
||||
Groups []Group `json:"groups"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
Name string `json:"name"`
|
||||
Tag string `json:"tag"`
|
||||
Skip bool `json:"skip"`
|
||||
Comment string `json:"comment"`
|
||||
Tests []string `json:"tests"`
|
||||
}
|
||||
|
||||
func ParseTestsStruct() (TestsStructure, error) {
|
||||
var testStruct TestsStructure
|
||||
if err := json.Unmarshal(testStructData, &testStruct); err != nil {
|
||||
return TestsStructure{}, fmt.Errorf("failed to parse tests struct: %w", err)
|
||||
}
|
||||
|
||||
return testStruct, nil
|
||||
}
|
17
internal/templates/resources/md-template.gotmpl
Normal file
17
internal/templates/resources/md-template.gotmpl
Normal file
|
@ -0,0 +1,17 @@
|
|||
# S3 Protocol Compatibility
|
||||
|
||||
Reference:
|
||||
* [AWS S3 API Reference](https://docs.aws.amazon.com/AmazonS3/latest/API/s3-api.pdf)
|
||||
|
||||
| | Legend |
|
||||
|---|--------|{{range .Legend}}
|
||||
| {{colorToCircle .Color}} | {{.Description}} |{{end}}
|
||||
|
||||
|
||||
{{range .TagGroups}}
|
||||
## {{.Name}}
|
||||
|
||||
| | Name | Comments |
|
||||
|---|------|----------|{{range .Tests}}
|
||||
| {{colorToCircle .Color}} | {{.Name}} | {{.Comment}} |{{end}}
|
||||
{{end}}
|
6
internal/templates/resources/txt-template.gotmpl
Normal file
6
internal/templates/resources/txt-template.gotmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
# S3 Protocol Compatibility
|
||||
{{range .TagGroups}}
|
||||
## {{.Name}}
|
||||
{{range .Tests}}
|
||||
{{colorToTerminal .Color}}{{.Name}}: {{.Passed}}/{{.Total}}; {{.Comment}} {{colorToTerminal "black"}} {{end}}
|
||||
{{end}}
|
86
internal/templates/templates.go
Normal file
86
internal/templates/templates.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
GreenColor = "green"
|
||||
YellowColor = "yellow"
|
||||
RedColor = "red"
|
||||
BlueColor = "blue"
|
||||
BlackColor = "black"
|
||||
)
|
||||
|
||||
const (
|
||||
blackTerminal = "\033[0m"
|
||||
redTerminal = "\033[31m"
|
||||
greenTerminal = "\033[32m"
|
||||
yellowTerminal = "\033[33m"
|
||||
blueTerminal = "\033[34m"
|
||||
)
|
||||
|
||||
const (
|
||||
greenCircle = "🟢"
|
||||
yellowCircle = "🟡"
|
||||
redCircle = "🔴"
|
||||
blueCircle = "🔵"
|
||||
blackCircle = "⚫"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed resources/txt-template.gotmpl
|
||||
txtTemplate []byte
|
||||
//go:embed resources/md-template.gotmpl
|
||||
mdTemplate []byte
|
||||
)
|
||||
|
||||
func GetTemplate(outFormat string) (*template.Template, error) {
|
||||
var templateData []byte
|
||||
|
||||
switch outFormat {
|
||||
case "txt":
|
||||
templateData = txtTemplate
|
||||
case "md":
|
||||
templateData = mdTemplate
|
||||
default:
|
||||
return nil, fmt.Errorf("unkonwn output format: %s", outFormat)
|
||||
}
|
||||
|
||||
return template.New("out-format").Funcs(template.FuncMap{
|
||||
"colorToCircle": ColorToCircle,
|
||||
"colorToTerminal": ColorToTerminal,
|
||||
}).Parse(string(templateData))
|
||||
}
|
||||
|
||||
func ColorToCircle(color string) string {
|
||||
switch color {
|
||||
case GreenColor:
|
||||
return greenCircle
|
||||
case YellowColor:
|
||||
return yellowCircle
|
||||
case RedColor:
|
||||
return redCircle
|
||||
case BlueColor:
|
||||
return blueCircle
|
||||
default:
|
||||
return blackCircle
|
||||
}
|
||||
}
|
||||
|
||||
func ColorToTerminal(color string) string {
|
||||
switch color {
|
||||
case GreenColor:
|
||||
return greenTerminal
|
||||
case YellowColor:
|
||||
return yellowTerminal
|
||||
case RedColor:
|
||||
return redTerminal
|
||||
case BlueColor:
|
||||
return blueTerminal
|
||||
default:
|
||||
return blackTerminal
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue