mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
284 lines
6.5 KiB
Go
284 lines
6.5 KiB
Go
// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley
|
|
// https://pb33f.io
|
|
|
|
package bundler
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"log"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pb33f/libopenapi"
|
|
"github.com/pb33f/libopenapi/datamodel"
|
|
"github.com/pb33f/libopenapi/utils"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestBundleDocument_DigitalOcean(t *testing.T) {
|
|
|
|
// test the mother of all exploded specs.
|
|
tmp, _ := os.MkdirTemp("", "openapi")
|
|
cmd := exec.Command("git", "clone", "https://github.com/digitalocean/openapi", tmp)
|
|
defer os.RemoveAll(filepath.Join(tmp, "openapi"))
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
log.Fatalf("cmd.Run() failed with %s\n", err)
|
|
}
|
|
|
|
spec, _ := filepath.Abs(filepath.Join(tmp+"/specification", "DigitalOcean-public.v2.yaml"))
|
|
digi, _ := os.ReadFile(spec)
|
|
|
|
doc, err := libopenapi.NewDocumentWithConfiguration([]byte(digi), &datamodel.DocumentConfiguration{
|
|
SpecFilePath: spec,
|
|
BasePath: tmp + "/specification",
|
|
ExtractRefsSequentially: true,
|
|
Logger: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
|
Level: slog.LevelWarn,
|
|
})),
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
v3Doc, errs := doc.BuildV3Model()
|
|
if len(errs) > 0 {
|
|
panic(errs)
|
|
}
|
|
|
|
bytes, e := BundleDocument(&v3Doc.Model)
|
|
|
|
assert.NoError(t, e)
|
|
assert.False(t, strings.Contains("$ref", string(bytes)), "should not contain $ref")
|
|
|
|
}
|
|
|
|
func TestBundleDocument_Circular(t *testing.T) {
|
|
|
|
digi, _ := os.ReadFile("../test_specs/circular-tests.yaml")
|
|
|
|
var logs []byte
|
|
byteBuf := bytes.NewBuffer(logs)
|
|
|
|
config := &datamodel.DocumentConfiguration{
|
|
ExtractRefsSequentially: true,
|
|
Logger: slog.New(slog.NewJSONHandler(byteBuf, &slog.HandlerOptions{
|
|
Level: slog.LevelWarn,
|
|
})),
|
|
}
|
|
doc, err := libopenapi.NewDocumentWithConfiguration(digi, config)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
v3Doc, errs := doc.BuildV3Model()
|
|
|
|
// three circular ref issues.
|
|
assert.Len(t, errs, 3)
|
|
|
|
bytes, e := BundleDocument(&v3Doc.Model)
|
|
assert.NoError(t, e)
|
|
if runtime.GOOS != "windows" {
|
|
assert.Len(t, *doc.GetSpecInfo().SpecBytes, 1563)
|
|
} else {
|
|
assert.Len(t, *doc.GetSpecInfo().SpecBytes, 1637)
|
|
}
|
|
assert.Len(t, bytes, 2016)
|
|
|
|
logEntries := strings.Split(byteBuf.String(), "\n")
|
|
if len(logEntries) == 1 && logEntries[0] == "" {
|
|
logEntries = []string{}
|
|
}
|
|
|
|
assert.Len(t, logEntries, 0)
|
|
}
|
|
|
|
func TestBundleBytes(t *testing.T) {
|
|
|
|
digi, _ := os.ReadFile("../test_specs/circular-tests.yaml")
|
|
|
|
var logs []byte
|
|
byteBuf := bytes.NewBuffer(logs)
|
|
|
|
config := &datamodel.DocumentConfiguration{
|
|
ExtractRefsSequentially: true,
|
|
Logger: slog.New(slog.NewJSONHandler(byteBuf, &slog.HandlerOptions{
|
|
Level: slog.LevelWarn,
|
|
})),
|
|
}
|
|
|
|
bytes, e := BundleBytes(digi, config)
|
|
assert.Error(t, e)
|
|
assert.Len(t, bytes, 2016)
|
|
|
|
logEntries := strings.Split(byteBuf.String(), "\n")
|
|
if len(logEntries) == 1 && logEntries[0] == "" {
|
|
logEntries = []string{}
|
|
}
|
|
|
|
assert.Len(t, logEntries, 0)
|
|
}
|
|
|
|
func TestBundleBytes_Invalid(t *testing.T) {
|
|
digi := []byte(`openapi: 3.1.0
|
|
components:
|
|
schemas:
|
|
toto:
|
|
$ref: bork`)
|
|
|
|
var logs []byte
|
|
byteBuf := bytes.NewBuffer(logs)
|
|
|
|
config := &datamodel.DocumentConfiguration{
|
|
ExtractRefsSequentially: true,
|
|
Logger: slog.New(slog.NewJSONHandler(byteBuf, &slog.HandlerOptions{
|
|
Level: slog.LevelWarn,
|
|
})),
|
|
}
|
|
|
|
_, e := BundleBytes(digi, config)
|
|
require.Error(t, e)
|
|
unwrap := utils.UnwrapErrors(e)
|
|
require.Len(t, unwrap, 2)
|
|
assert.ErrorIs(t, unwrap[0], ErrInvalidModel)
|
|
unwrapNext := utils.UnwrapErrors(unwrap[1])
|
|
require.Len(t, unwrapNext, 2)
|
|
assert.Equal(t, "component `bork` does not exist in the specification", unwrapNext[0].Error())
|
|
assert.Equal(t, "cannot resolve reference `bork`, it's missing: $bork [5:7]", unwrapNext[1].Error())
|
|
|
|
logEntries := strings.Split(byteBuf.String(), "\n")
|
|
if len(logEntries) == 1 && logEntries[0] == "" {
|
|
logEntries = []string{}
|
|
}
|
|
|
|
assert.Len(t, logEntries, 0)
|
|
}
|
|
|
|
func TestBundleBytes_CircularArray(t *testing.T) {
|
|
|
|
digi := []byte(`openapi: 3.1.0
|
|
info:
|
|
title: FailureCases
|
|
version: 0.1.0
|
|
servers:
|
|
- url: http://localhost:35123
|
|
description: The default server.
|
|
paths:
|
|
/test:
|
|
get:
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
components:
|
|
schemas:
|
|
Obj:
|
|
type: object
|
|
properties:
|
|
children:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Obj'
|
|
required:
|
|
- children`)
|
|
|
|
var logs []byte
|
|
byteBuf := bytes.NewBuffer(logs)
|
|
|
|
config := &datamodel.DocumentConfiguration{
|
|
ExtractRefsSequentially: true,
|
|
IgnoreArrayCircularReferences: true,
|
|
Logger: slog.New(slog.NewJSONHandler(byteBuf, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
})),
|
|
}
|
|
|
|
bytes, e := BundleBytes(digi, config)
|
|
assert.NoError(t, e)
|
|
assert.Len(t, bytes, 537)
|
|
|
|
logEntries := strings.Split(byteBuf.String(), "\n")
|
|
assert.Len(t, logEntries, 10)
|
|
}
|
|
|
|
func TestBundleBytes_CircularFile(t *testing.T) {
|
|
|
|
digi := []byte(`openapi: 3.1.0
|
|
info:
|
|
title: FailureCases
|
|
version: 0.1.0
|
|
servers:
|
|
- url: http://localhost:35123
|
|
description: The default server.
|
|
paths:
|
|
/test:
|
|
get:
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
components:
|
|
schemas:
|
|
Obj:
|
|
type: object
|
|
properties:
|
|
children:
|
|
$ref: '../test_specs/circular-tests.yaml#/components/schemas/One'`)
|
|
|
|
var logs []byte
|
|
byteBuf := bytes.NewBuffer(logs)
|
|
|
|
config := &datamodel.DocumentConfiguration{
|
|
BasePath: ".",
|
|
ExtractRefsSequentially: true,
|
|
Logger: slog.New(slog.NewJSONHandler(byteBuf, &slog.HandlerOptions{
|
|
Level: slog.LevelDebug,
|
|
})),
|
|
}
|
|
|
|
bytes, e := BundleBytes(digi, config)
|
|
assert.Error(t, e)
|
|
assert.Len(t, bytes, 458)
|
|
|
|
logEntries := strings.Split(byteBuf.String(), "\n")
|
|
assert.Len(t, logEntries, 13)
|
|
}
|
|
|
|
func TestBundleBytes_Bad(t *testing.T) {
|
|
bytes, e := BundleBytes(nil, nil)
|
|
assert.Error(t, e)
|
|
assert.Nil(t, bytes)
|
|
}
|
|
|
|
func TestBundleBytes_RootDocumentRefs(t *testing.T) {
|
|
spec, err := os.ReadFile("../test_specs/ref-followed.yaml")
|
|
assert.NoError(t, err)
|
|
|
|
{ // Making sure indentation is identical
|
|
doc, err := libopenapi.NewDocument(spec)
|
|
assert.NoError(t, err)
|
|
|
|
v3Doc, errs := doc.BuildV3Model()
|
|
assert.NoError(t, errors.Join(errs...))
|
|
|
|
spec, err = v3Doc.Model.Render()
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
config := &datamodel.DocumentConfiguration{
|
|
BasePath: ".",
|
|
ExtractRefsSequentially: true,
|
|
}
|
|
|
|
bundledSpec, err := BundleBytes(spec, config)
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, string(spec), string(bundledSpec))
|
|
}
|