Coverage Report
Generated on Fri, 04 Jul 2025 09:52:48 UTC with gocov-html
Report Overview
github.com/xgmsx/go-merge-openapi 0.0% 0/15
github.com/xgmsx/go-merge-openapi/internal/openapi 57.0% 45/79
github.com/xgmsx/go-merge-openapi/pkg/merge 77.8% 7/9

This coverage report has been generated with the following command:

gocov test github.com/xgmsx/go-merge-openapi github.com/xgmsx/go-merge-openapi/internal/openapi github.com/xgmsx/go-merge-openapi/pkg/merge | gocov-html 
Package Overview: github.com/xgmsx/go-merge-openapi 0.0%

Please select a function to see what's left for testing.

main(...) github.com/xgmsx/go-merge-openapi/main.go 0.0% 0/15
func main
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/main.go:

12
func main() {
13
        var configPath, outputPath string
14
        flag.StringVar(&configPath, "config", "", "Path to OpenAPI config file")
15
        flag.StringVar(&outputPath, "output", "", "Optional output file path")
16
        flag.Parse()
17
18
        if configPath == "" {
19
                log.Fatal("missing -config argument")
20
        }
21
22
        merged, err := merge.Merge(configPath)
23
        if err != nil {
24
                log.Fatalf("merge failed: %v", err)
25
        }
26
27
        if outputPath == "" {
28
                base := strings.TrimSuffix(filepath.Base(configPath), filepath.Ext(configPath))
29
                outputPath = filepath.Join(filepath.Dir(configPath), base+".merged.yaml")
30
        }
31
32
        if err := merge.Write(merged, filepath.Clean(outputPath)); err != nil {
33
                log.Fatalf("write failed: %v", err)
34
        }
35
36
        log.Print("merged to", outputPath)
37
}
Package Overview: github.com/xgmsx/go-merge-openapi/internal/openapi 57.0%

Please select a function to see what's left for testing.

ResolveReferences(...) github.com/xgmsx/go-merge-openapi/internal/openapi/resolve.go 77.8% 14/18
WriteSpec(...) github.com/xgmsx/go-merge-openapi/internal/openapi/write.go 76.9% 10/13
resolveReference(...) github.com/xgmsx/go-merge-openapi/internal/openapi/resolve.go 68.2% 15/22
LoadYAMLNode(...) github.com/xgmsx/go-merge-openapi/internal/openapi/load.go 66.7% 6/9
extractFragment(...) github.com/xgmsx/go-merge-openapi/internal/openapi/resolve.go 0.0% 0/17
func ResolveReferences
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/internal/openapi/resolve.go:

11
func ResolveReferences(node *yaml.Node, baseDir string, visited map[string]*yaml.Node) error {
12
        if node == nil {
13
                return nil
14
        }
15
16
        switch node.Kind {
17
        case yaml.MappingNode:
18
                // Check if this is a $ref object
19
                if len(node.Content) >= 2 && node.Content[0].Value == "$ref" {
20
                        refPath := node.Content[1].Value
21
                        resolved, err := resolveReference(refPath, baseDir, visited)
22
                        if err != nil {
23
                                return err
24
                        }
25
                        *node = *resolved
26
                        return nil
27
                }
28
29
                // Traverse each key-value pair
30
                for i := 0; i < len(node.Content); i += 2 {
31
                        if i+1 < len(node.Content) {
32
                                if err := ResolveReferences(node.Content[i+1], baseDir, visited); err != nil {
33
                                        return err
34
                                }
35
                        }
36
                }
37
38
        case yaml.SequenceNode:
39
                for _, item := range node.Content {
40
                        if err := ResolveReferences(item, baseDir, visited); err != nil {
41
                                return err
42
                        }
43
                }
44
        }
45
46
        return nil
47
}
func WriteSpec
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/internal/openapi/write.go:

13
func WriteSpec(node *yaml.Node, path string) error {
14
        var data []byte
15
        var err error
16
17
        ext := strings.ToLower(filepath.Ext(path))
18
        switch ext {
19
        case ".json":
20
                var spec interface{}
21
                if err := node.Decode(&spec); err != nil {
22
                        return fmt.Errorf("failed to decode YAML node: %w", err)
23
                }
24
                data, err = json.MarshalIndent(spec, "", "  ")
25
        case ".yaml", ".yml":
26
                data, err = yaml.Marshal(node)
27
        default:
28
                data, err = yaml.Marshal(node)
29
        }
30
31
        if err != nil {
32
                return fmt.Errorf("failed to marshal spec: %w", err)
33
        }
34
35
        return os.WriteFile(filepath.Clean(path), data, 0o600)
36
}
func resolveReference
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/internal/openapi/resolve.go:

49
func resolveReference(refPath, baseDir string, visited map[string]*yaml.Node) (*yaml.Node, error) {
50
        var filePath, fragment string
51
52
        if strings.Contains(refPath, "#") {
53
                parts := strings.SplitN(refPath, "#", 2)
54
                filePath = parts[0]
55
                fragment = parts[1]
56
        } else {
57
                filePath = refPath
58
        }
59
60
        if !filepath.IsAbs(filePath) {
61
                filePath = filepath.Join(baseDir, filePath)
62
        }
63
64
        cacheKey := filePath
65
        if cachedNode, ok := visited[cacheKey]; ok {
66
                if fragment != "" {
67
                        return extractFragment(cachedNode, fragment)
68
                }
69
                return cachedNode, nil
70
        }
71
72
        refNode, err := LoadYAMLNode(filePath)
73
        if err != nil {
74
                return nil, fmt.Errorf("failed to load referenced file '%s': %w", filePath, err)
75
        }
76
77
        visited[cacheKey] = refNode
78
79
        if err := ResolveReferences(refNode, filepath.Dir(filePath), visited); err != nil {
80
                return nil, err
81
        }
82
83
        if fragment != "" {
84
                return extractFragment(refNode, fragment)
85
        }
86
87
        return refNode, nil
88
}
func LoadYAMLNode
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/internal/openapi/load.go:

11
func LoadYAMLNode(path string) (*yaml.Node, error) {
12
        data, err := os.ReadFile(filepath.Clean(path))
13
        if err != nil {
14
                return nil, err
15
        }
16
17
        var node yaml.Node
18
        if err := yaml.Unmarshal(data, &node); err != nil {
19
                return nil, fmt.Errorf("failed to parse YAML: %w", err)
20
        }
21
22
        if node.Kind == yaml.DocumentNode && len(node.Content) > 0 {
23
                return node.Content[0], nil
24
        }
25
26
        return &node, nil
27
}
func extractFragment
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/internal/openapi/resolve.go:

90
func extractFragment(node *yaml.Node, fragment string) (*yaml.Node, error) {
91
        if fragment == "" || fragment == "/" {
92
                return node, nil
93
        }
94
95
        fragment = strings.TrimPrefix(fragment, "/")
96
97
        for _, part := range strings.Split(fragment, "/") {
98
                if part == "" {
99
                        continue
100
                }
101
102
                if node.Kind != yaml.MappingNode {
103
                        return nil, fmt.Errorf("cannot navigate fragment path '%s': not a mapping", fragment)
104
                }
105
106
                found := false
107
                for i := 0; i < len(node.Content); i += 2 {
108
                        if i+1 < len(node.Content) && node.Content[i].Value == part {
109
                                node = node.Content[i+1]
110
                                found = true
111
                                break
112
                        }
113
                }
114
115
                if !found {
116
                        return nil, fmt.Errorf("fragment path '%s' not found", fragment)
117
                }
118
        }
119
120
        return node, nil
121
}
Package Overview: github.com/xgmsx/go-merge-openapi/pkg/merge 77.8%

Please select a function to see what's left for testing.

Write(...) github.com/xgmsx/go-merge-openapi/pkg/merge/merger.go 100.0% 1/1
Merge(...) github.com/xgmsx/go-merge-openapi/pkg/merge/merger.go 75.0% 6/8
func Write
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/pkg/merge/merger.go:

26
func Write(node *yaml.Node, outputPath string) error {
27
        return openapi.WriteSpec(node, outputPath)
28
}
func Merge
Back

In /home/runner/work/go-merge-openapi/go-merge-openapi/pkg/merge/merger.go:

11
func Merge(inputPath string) (*yaml.Node, error) {
12
        baseDir := filepath.Dir(inputPath)
13
        node, err := openapi.LoadYAMLNode(inputPath)
14
        if err != nil {
15
                return nil, fmt.Errorf("load failed: %w", err)
16
        }
17
18
        visited := make(map[string]*yaml.Node)
19
        if err := openapi.ResolveReferences(node, baseDir, visited); err != nil {
20
                return nil, fmt.Errorf("resolve failed: %w", err)
21
        }
22
23
        return node, nil
24
}
Report Total
50.5%