Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions doc/__fixtures__/opencli/1.rootCmd/output.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
opencli: 1.0.0
info:
description: Root long description
summary: Root short description
title: root
version: ""
conventions:
groupOptions: true
optionSeparator: ' '
options:
- aliases:
- h
description: help for root
name: help
- aliases:
- r
arguments:
- name: rootflag
name: rootflag
recursive: true
- aliases:
- t
arguments:
- description: help message for parent flag strtwo
name: strtwo
description: help message for parent flag strtwo
name: strtwo
recursive: true
commands:
- arguments:
- name: action
required: false
description: Performs a dummy action
name: dummy
options:
- aliases:
- h
description: help for dummy
name: help
- aliases:
- say
arguments:
- name: string to echo
required: false
commands:
- arguments:
- name: string to print
required: false
description: second sub command for echo
name: echosub
- arguments:
- name: '# times'
required: false
- name: string to echo
required: false
description: Echo anything to the screen more times
name: times
options:
- aliases:
- c
description: help message for flag booltwo
name: booltwo
- aliases:
- j
arguments:
- description: help message for flag inttwo
name: inttwo
description: help message for flag inttwo
name: inttwo
- aliases:
- t
arguments:
- description: help message for child flag strtwo
name: strtwo
description: help message for child flag strtwo
name: strtwo
recursive: true
description: Echo anything to the screen
examples:
- Just run cobra-test echo
name: echo
options:
- aliases:
- b
description: help message for flag boolone
name: boolone
- aliases:
- h
description: help for echo
name: help
- aliases:
- i
arguments:
- description: help message for flag intone
name: intone
description: help message for flag intone
name: intone
- aliases:
- p
description: help message for flag persistentbool
name: persistentbool
recursive: true
- aliases:
- s
arguments:
- description: help message for flag strone
name: strone
description: help message for flag strone
name: strone
recursive: true
- arguments:
- name: string to print
required: false
description: Print anything to the screen
name: print
options:
- aliases:
- b
description: help message for flag boolthree
name: boolthree
- aliases:
- i
arguments:
- description: help message for flag intthree
name: intthree
description: help message for flag intthree
name: intthree
- aliases:
- s
arguments:
- description: help message for flag strthree
name: strthree
description: help message for flag strthree
name: strthree
recursive: true
95 changes: 95 additions & 0 deletions doc/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
package doc

import (
"os"
"path/filepath"
"sort"
"strings"
"testing"

"github.com/spf13/cobra"
"go.yaml.in/yaml/v3"
)

func emptyRun(*cobra.Command, []string) {}
Expand Down Expand Up @@ -103,3 +107,94 @@ func checkStringOmits(t *testing.T, got, expected string) {
t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
}
}

// loadFixture loads a YAML fixture file from __fixtures__ directory and returns its contents
func loadFixture(t *testing.T, parts ...string) []byte {
path := filepath.Join(append([]string{"__fixtures__"}, parts...)...)
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("Failed to read fixture file %s: %v", path, err)
}
return data
}

// compareNormalizedYAML compares two YAML byte slices after normalizing them
func compareNormalizedYAML(t *testing.T, expected, actual []byte) {
expectedNormalized, err := normalizeYAML(expected)
if err != nil {
t.Fatalf("Failed to normalize expected YAML: %v", err)
}

actualNormalized, err := normalizeYAML(actual)
if err != nil {
t.Fatalf("Failed to normalize actual YAML: %v", err)
}

if string(expectedNormalized) != string(actualNormalized) {
t.Errorf("Generated OpenCLI does not match fixture")
t.Logf("Expected:\n%s", string(expectedNormalized))
t.Logf("Actual:\n%s", string(actualNormalized))
}
}

// normalizeYAML normalizes YAML data by recursively sorting map keys
// and re-marshaling to ensure consistent ordering
func normalizeYAML(data []byte) ([]byte, error) {
var v interface{}
if err := yaml.Unmarshal(data, &v); err != nil {
return nil, err
}
normalized := sortYAMLKeys(v)
return yaml.Marshal(normalized)
}

// sortYAMLKeys recursively sorts map keys in a YAML structure
func sortYAMLKeys(v interface{}) interface{} {
switch val := v.(type) {
case map[interface{}]interface{}:
// Create a new map with sorted keys
sorted := make(map[interface{}]interface{})
keys := make([]string, 0, len(val))
keyMap := make(map[string]interface{})

// Collect all keys and convert to strings for sorting
for k := range val {
keyStr := toString(k)
keys = append(keys, keyStr)
keyMap[keyStr] = k
}

// Sort keys
sort.Strings(keys)

// Rebuild map with sorted keys, recursively sorting values
for _, keyStr := range keys {
originalKey := keyMap[keyStr]
sorted[originalKey] = sortYAMLKeys(val[originalKey])
}
return sorted
case []interface{}:
// Recursively sort elements in slices
sorted := make([]interface{}, len(val))
for i, item := range val {
sorted[i] = sortYAMLKeys(item)
}
return sorted
default:
// Primitive types, return as-is
return v
}
}

// toString converts a key to string for sorting
func toString(k interface{}) string {
switch v := k.(type) {
case string:
return v
case int:
return string(rune(v))
default:
// For other types, try to convert to string
return ""
}
}
Loading