Files
libopenapi/bundler/bundler_test.go
quobix 70f406b6cf Added NodeMap to all low level objects.
this sync map tracks all the nodes that apply to this object.
2024-08-05 09:29:07 -04:00

283 lines
6.4 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{
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))
}