bashing through usecases and updating tests as we go.

so many things that can go wrong. have to catch them all.

Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
quobix
2023-10-19 15:18:33 -04:00
parent 054103b733
commit b295e8fd5c
11 changed files with 404 additions and 181 deletions

View File

@@ -189,6 +189,7 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
var p string
uri := strings.Split(value, "#/")
if strings.HasPrefix(value, "http") || filepath.IsAbs(value) {
if len(uri) == 2 {
_, p = utils.ConvertComponentIdIntoFriendlyPathSearch(fmt.Sprintf("#/%s", uri[1]))

View File

@@ -62,8 +62,17 @@ func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Re
}
} else {
if !strings.Contains(componentId, "#") {
// does it contain a file extension?
fileExt := filepath.Ext(componentId)
if fileExt != "" {
return index.lookupRolodex(uri)
}
// root search
return index.FindComponentInRoot(componentId)
}
return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[0]))
}
@@ -368,6 +377,7 @@ func (index *SpecIndex) lookupRolodex(uri []string) *Reference {
if index.specAbsolutePath != "" {
if index.config.BaseURL != nil {
// consider the file remote.
//if strings.Contains(file, "../../") {
// extract the base path from the specAbsolutePath for this index.
@@ -384,8 +394,11 @@ func (index *SpecIndex) lookupRolodex(uri []string) *Reference {
//absoluteFileLocation = loc
} else {
panic("nooooooo")
absoluteFileLocation, _ = filepath.Abs(filepath.Join(index.config.BaseURL.Path, file))
// consider the file local
dir := filepath.Dir(index.config.SpecAbsolutePath)
absoluteFileLocation, _ = filepath.Abs(filepath.Join(dir, file))
}
} else {
absoluteFileLocation = file

View File

@@ -43,7 +43,7 @@ func TestSpecIndex_CheckCircularIndex(t *testing.T) {
_ = yaml.Unmarshal([]byte(yml), &rootNode)
cf := CreateOpenAPIIndexConfig()
cf.AvoidBuildIndex = true
cf.AvoidCircularReferenceCheck = true
cf.BasePath = "../test_specs"
rolo := NewRolodex(cf)

View File

@@ -268,8 +268,8 @@ type SpecIndex struct {
enumCount int
descriptionCount int
summaryCount int
seenRemoteSources map[string]*yaml.Node
seenLocalSources map[string]*yaml.Node
//seenRemoteSources map[string]*yaml.Node
//seenLocalSources map[string]*yaml.Node
refLock sync.Mutex
componentLock sync.RWMutex
errorLock sync.RWMutex

View File

@@ -82,8 +82,8 @@ func boostrapIndexCollections(rootNode *yaml.Node, index *SpecIndex) {
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
index.polymorphicRefs = make(map[string]*Reference)
index.refsWithSiblings = make(map[string]Reference)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenLocalSources = make(map[string]*yaml.Node)
//index.seenRemoteSources = make(map[string]*yaml.Node)
//index.seenLocalSources = make(map[string]*yaml.Node)
index.opServersRefs = make(map[string]map[string][]*Reference)
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
index.componentIndexChan = make(chan bool)

View File

@@ -258,10 +258,6 @@ func (r *Rolodex) IndexTheRolodex() error {
return nil
}
// disable index building, it will need to be run after the rolodex indexed
// at a high level.
r.indexConfig.AvoidBuildIndex = true
var caughtErrors []error
var indexBuildQueue []*SpecIndex
@@ -373,6 +369,19 @@ func (r *Rolodex) IndexTheRolodex() error {
if r.rootNode != nil {
// if there is a base path, then we need to set the root spec config to point to a theoretical root.yaml
// which does not exist, but is used to formulate the absolute path to root references correctly.
if r.indexConfig.BasePath != "" && r.indexConfig.BaseURL == nil {
basePath := r.indexConfig.BasePath
if !filepath.IsAbs(basePath) {
basePath, _ = filepath.Abs(basePath)
}
r.indexConfig.SpecAbsolutePath = filepath.Join(basePath, "root.yaml")
}
// todo: variation with no base path, but a base URL.
index := NewSpecIndexWithConfig(r.rootNode, r.indexConfig)
resolver := NewResolver(index)
if r.indexConfig.IgnoreArrayCircularReferences {
@@ -382,9 +391,7 @@ func (r *Rolodex) IndexTheRolodex() error {
resolver.IgnorePolymorphicCircularReferences()
}
if !r.indexConfig.AvoidBuildIndex {
index.BuildIndex()
}
if !r.indexConfig.AvoidCircularReferenceCheck {
resolvingErrors := resolver.CheckForCircularReferences()
@@ -393,10 +400,14 @@ func (r *Rolodex) IndexTheRolodex() error {
}
}
r.rootIndex = index
if len(index.refErrors) > 0 {
caughtErrors = append(caughtErrors, index.refErrors...)
}
}
r.indexingDuration = time.Since(started)
r.indexed = true
r.caughtErrors = caughtErrors
r.built = true
return errors.Join(caughtErrors...)
}

View File

@@ -17,6 +17,7 @@ import (
)
type LocalFS struct {
indexConfig *SpecIndexConfig
entryPointDirectory string
baseDirectory string
Files map[string]RolodexFile
@@ -28,7 +29,18 @@ func (l *LocalFS) GetFiles() map[string]RolodexFile {
return l.Files
}
func (l *LocalFS) GetErrors() []error {
return l.readingErrors
}
func (l *LocalFS) Open(name string) (fs.File, error) {
if l.indexConfig != nil && !l.indexConfig.AllowFileLookup {
return nil, &fs.PathError{Op: "open", Path: name,
Err: fmt.Errorf("file lookup for '%s' not allowed, set the index configuration "+
"to AllowFileLookup to be true", name)}
}
if !filepath.IsAbs(name) {
var absErr error
name, absErr = filepath.Abs(filepath.Join(l.baseDirectory, name))

View File

@@ -27,6 +27,7 @@ type RemoteFS struct {
rootURLParsed *url.URL
RemoteHandlerFunc RemoteURLHandler
Files syncmap.Map
ProcessingFiles syncmap.Map
FetchTime int64
FetchChannel chan *RemoteFile
remoteWg sync.WaitGroup
@@ -235,6 +236,10 @@ func (i *RemoteFS) GetFiles() map[string]RolodexFile {
return files
}
func (i *RemoteFS) GetErrors() []error {
return i.remoteErrors
}
func (i *RemoteFS) seekRelatives(file *RemoteFile) {
extractedRefs := ExtractRefs(string(file.data))
@@ -288,6 +293,11 @@ func (i *RemoteFS) seekRelatives(file *RemoteFile) {
func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
if i.indexConfig != nil && !i.indexConfig.AllowRemoteLookup {
return nil, fmt.Errorf("remote lookup for '%s' is not allowed, please set "+
"AllowRemoteLookup to true as part of the index configuration", remoteURL)
}
remoteParsedURL, err := url.Parse(remoteURL)
if err != nil {
return nil, err
@@ -298,6 +308,20 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
return r.(*RemoteFile), nil
}
// if we're processing, we need to block and wait for the file to be processed
// try path first
if _, ok := i.ProcessingFiles.Load(remoteParsedURL.Path); ok {
i.logger.Debug("waiting for existing fetch to complete", "file", remoteURL, "remoteURL", remoteParsedURL.String())
for {
if wf, ko := i.Files.Load(remoteParsedURL.Path); ko {
return wf.(*RemoteFile), nil
}
}
}
// add to processing
i.ProcessingFiles.Store(remoteParsedURL.Path, true)
fileExt := ExtractFileType(remoteParsedURL.Path)
if fileExt == UNSUPPORTED {
@@ -314,7 +338,7 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
}
}
i.logger.Debug("Loading remote file", "file", remoteURL, "remoteURL", remoteParsedURL.String())
i.logger.Debug("loading remote file", "file", remoteURL, "remoteURL", remoteParsedURL.String())
// no handler func? use the default client.
if i.RemoteHandlerFunc == nil {
@@ -323,20 +347,32 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
response, clientErr := i.RemoteHandlerFunc(remoteParsedURL.String())
if clientErr != nil {
i.remoteErrors = append(i.remoteErrors, clientErr)
// remove from processing
i.ProcessingFiles.Delete(remoteParsedURL.Path)
if response != nil {
i.logger.Error("client error", "error", clientErr, "status", response.StatusCode)
} else {
i.logger.Error("no response for request", "error", clientErr.Error())
i.logger.Error("client error, empty body", "error", clientErr.Error())
}
return nil, clientErr
}
responseBytes, readError := io.ReadAll(response.Body)
if readError != nil {
// remove from processing
i.ProcessingFiles.Delete(remoteParsedURL.Path)
return nil, readError
}
if response.StatusCode >= 400 {
// remove from processing
i.ProcessingFiles.Delete(remoteParsedURL.Path)
i.logger.Error("Unable to fetch remote document",
"file", remoteParsedURL.Path, "status", response.StatusCode, "resp", string(responseBytes))
return nil, fmt.Errorf("unable to fetch remote document: %s", string(responseBytes))
@@ -344,6 +380,8 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
absolutePath, pathErr := filepath.Abs(remoteParsedURL.Path)
if pathErr != nil {
// remove from processing
i.ProcessingFiles.Delete(remoteParsedURL.Path)
return nil, pathErr
}
@@ -394,11 +432,16 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
i.Files.Store(absolutePath, remoteFile)
if len(remoteFile.data) > 0 {
i.logger.Debug("successfully loaded file", "file", absolutePath)
}
i.seekRelatives(remoteFile)
idx.BuildIndex()
// remove from processing
i.ProcessingFiles.Delete(remoteParsedURL.Path)
if !i.remoteRunning {
return remoteFile, errors.Join(i.remoteErrors...)
} else {

View File

@@ -24,12 +24,27 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) *
if strings.HasPrefix(uri[0], "http") {
roloLookup = fullRef.FullDefinition
} else {
if filepath.IsAbs(uri[0]) {
roloLookup = uri[0]
} else {
if filepath.Ext(absPath) != "" {
absPath = filepath.Dir(absPath)
}
roloLookup, _ = filepath.Abs(filepath.Join(absPath, uri[0]))
}
}
}
ref = fmt.Sprintf("#/%s", uri[1])
} else {
if filepath.IsAbs(uri[0]) {
roloLookup = uri[0]
} else {
if filepath.Ext(absPath) != "" {
absPath = filepath.Dir(absPath)
}
roloLookup, _ = filepath.Abs(filepath.Join(absPath, uri[0]))
}
ref = uri[0]
}
@@ -65,6 +80,15 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) *
return s
}
}
// does component exist in the root?
node, _ := rFile.GetContentAsYAMLNode()
if node != nil {
found := idx.FindComponent(ref, node)
if found != nil {
return found
}
}
}
}

View File

@@ -430,10 +430,10 @@ func (index *SpecIndex) GetAllOperationsServers() map[string]map[string][]*Refer
return index.opServersRefs
}
// GetAllExternalIndexes will return all indexes for external documents
func (index *SpecIndex) GetAllExternalIndexes() map[string]*SpecIndex {
return index.externalSpecIndex
}
//// GetAllExternalIndexes will return all indexes for external documents
//func (index *SpecIndex) GetAllExternalIndexes() map[string]*SpecIndex {
// return index.externalSpecIndex
//}
// SetAllowCircularReferenceResolving will flip a bit that can be used by any consumers to determine if they want
// to allow or disallow circular references to be resolved or visited

View File

@@ -5,6 +5,7 @@ package index
import (
"fmt"
"github.com/pb33f/libopenapi/utils"
"log"
"log/slog"
"net/http"
@@ -64,7 +65,6 @@ func TestSpecIndex_ExtractRefsStripe(t *testing.T) {
assert.Len(t, index.GetRefsByLine(), 537)
assert.Len(t, index.GetLinesWithReferences(), 1972)
assert.Len(t, index.GetAllExternalDocuments(), 0)
assert.Len(t, index.GetAllExternalIndexes(), 0)
}
func TestSpecIndex_Asana(t *testing.T) {
@@ -97,7 +97,7 @@ func TestSpecIndex_DigitalOcean(t *testing.T) {
location := "https://raw.githubusercontent.com/digitalocean/openapi/main/specification"
baseURL, _ := url.Parse(location)
// create a new config that allows local and remote to be mixed up.
// create a new config that allows remote lookups.
cf := &SpecIndexConfig{}
cf.AvoidBuildIndex = true
cf.AllowRemoteLookup = true
@@ -121,7 +121,6 @@ func TestSpecIndex_DigitalOcean(t *testing.T) {
// create a handler that uses an env variable to capture any GITHUB_TOKEN in the OS ENV
// and inject it into the request header, so this does not fail when running lots of local tests.
if os.Getenv("GITHUB_TOKEN") != "" {
client := &http.Client{
Timeout: time.Second * 60,
}
@@ -130,7 +129,6 @@ func TestSpecIndex_DigitalOcean(t *testing.T) {
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("GITHUB_TOKEN")))
return client.Do(request)
})
}
// add remote filesystem
@@ -143,19 +141,7 @@ func TestSpecIndex_DigitalOcean(t *testing.T) {
files := remoteFS.GetFiles()
fileLen := len(files)
assert.Equal(t, 1646, fileLen)
//
//
//
//
//
//assert.NoError(t, indexedErr)
//
//index := rolo.GetRootIndex()
//rolo.CheckForCircularReferences()
//
//assert.Len(t, index.GetAllExternalIndexes(), 291)
//assert.NotNil(t, index)
assert.Len(t, remoteFS.GetErrors(), 0)
}
func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) {
@@ -163,69 +149,153 @@ func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) {
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"))
doLocal, _ := os.ReadFile(spec)
var rootNode yaml.Node
_ = yaml.Unmarshal(doLocal, &rootNode)
config := CreateOpenAPIIndexConfig()
config.BasePath = filepath.Join(tmp, "specification")
basePath := filepath.Join(tmp, "specification")
index := NewSpecIndexWithConfig(&rootNode, config)
// create a new config that allows local and remote to be mixed up.
cf := CreateOpenAPIIndexConfig()
cf.AvoidBuildIndex = true
cf.AllowRemoteLookup = true
cf.AvoidCircularReferenceCheck = true
cf.BasePath = basePath
// create a new rolodex
rolo := NewRolodex(cf)
// set the rolodex root node to the root node of the spec.
rolo.SetRootNode(&rootNode)
// configure the local filesystem.
fsCfg := LocalFSConfig{
BaseDirectory: cf.BasePath,
DirFS: os.DirFS(cf.BasePath),
}
// create a new local filesystem.
fileFS, fsErr := NewLocalFSWithConfig(&fsCfg)
assert.NoError(t, fsErr)
files := fileFS.GetFiles()
fileLen := len(files)
assert.Equal(t, 1684, fileLen)
rolo.AddLocalFS(basePath, fileFS)
rErr := rolo.IndexTheRolodex()
assert.NoError(t, rErr)
index := rolo.GetRootIndex()
assert.NotNil(t, index)
assert.Len(t, index.GetAllExternalIndexes(), 296)
ref := index.SearchIndexForReference("resources/apps/apps_list_instanceSizes.yml")
assert.NotNil(t, ref)
assert.Equal(t, "operationId", ref.Node.Content[0].Value)
ref = index.SearchIndexForReference("examples/ruby/domains_create.yml")
assert.NotNil(t, ref)
assert.Equal(t, "lang", ref.Node.Content[0].Value)
ref = index.SearchIndexForReference("../../shared/responses/server_error.yml")
assert.NotNil(t, ref)
assert.Equal(t, "description", ref.Node.Content[0].Value)
ref = index.SearchIndexForReference("../models/options.yml")
assert.NotNil(t, ref)
assert.Len(t, index.GetMappedReferencesSequenced(), 296)
assert.Len(t, index.GetMappedReferences(), 296)
assert.Len(t, fileFS.GetErrors(), 0)
}
func TestSpecIndex_DigitalOcean_LookupsNotAllowed(t *testing.T) {
asana, _ := os.ReadFile("../test_specs/digitalocean.yaml")
do, _ := os.ReadFile("../test_specs/digitalocean.yaml")
var rootNode yaml.Node
_ = yaml.Unmarshal(asana, &rootNode)
_ = yaml.Unmarshal(do, &rootNode)
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{
BaseURL: baseURL,
})
location := "https://raw.githubusercontent.com/digitalocean/openapi/main/specification"
baseURL, _ := url.Parse(location)
// create a new config that does not allow remote lookups.
cf := &SpecIndexConfig{}
cf.AvoidBuildIndex = true
cf.AvoidCircularReferenceCheck = true
cf.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelError,
}))
// setting this baseURL will override the base
cf.BaseURL = baseURL
// create a new rolodex
rolo := NewRolodex(cf)
// set the rolodex root node to the root node of the spec.
rolo.SetRootNode(&rootNode)
// create a new remote fs and set the config for indexing.
remoteFS, _ := NewRemoteFSWithConfig(cf)
// add remote filesystem
rolo.AddRemoteFS(location, remoteFS)
// index the rolodex.
indexedErr := rolo.IndexTheRolodex()
assert.Error(t, indexedErr)
assert.Len(t, utils.UnwrapErrors(indexedErr), 291)
index := rolo.GetRootIndex()
files := remoteFS.GetFiles()
fileLen := len(files)
assert.Equal(t, 0, fileLen)
assert.Len(t, remoteFS.GetErrors(), 0)
// no lookups allowed, bits have not been set, so there should just be a bunch of errors.
assert.Len(t, index.GetAllExternalIndexes(), 0)
assert.True(t, len(index.GetReferenceIndexErrors()) > 0)
}
func TestSpecIndex_BaseURLError(t *testing.T) {
asana, _ := os.ReadFile("../test_specs/digitalocean.yaml")
do, _ := os.ReadFile("../test_specs/digitalocean.yaml")
var rootNode yaml.Node
_ = yaml.Unmarshal(asana, &rootNode)
_ = yaml.Unmarshal(do, &rootNode)
// this should fail because the base url is not a valid url and digital ocean won't be able to resolve
// anything.
baseURL, _ := url.Parse("https://githerbs.com/fresh/herbs/for/you")
index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{
BaseURL: baseURL,
//AllowRemoteLookup: true,
//AllowFileLookup: true,
})
location := "https://githerbsandcoffeeandcode.com/fresh/herbs/for/you" // not gonna work bro.
baseURL, _ := url.Parse(location)
// create a new config that allows remote lookups.
cf := &SpecIndexConfig{}
cf.AvoidBuildIndex = true
cf.AllowRemoteLookup = true
cf.AvoidCircularReferenceCheck = true
cf.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelError,
}))
// setting this baseURL will override the base
cf.BaseURL = baseURL
// create a new rolodex
rolo := NewRolodex(cf)
// set the rolodex root node to the root node of the spec.
rolo.SetRootNode(&rootNode)
// create a new remote fs and set the config for indexing.
remoteFS, _ := NewRemoteFSWithConfig(cf)
// add remote filesystem
rolo.AddRemoteFS(location, remoteFS)
// index the rolodex.
indexedErr := rolo.IndexTheRolodex()
assert.Error(t, indexedErr)
assert.Len(t, utils.UnwrapErrors(indexedErr), 291)
files := remoteFS.GetFiles()
fileLen := len(files)
assert.Equal(t, 0, fileLen)
assert.GreaterOrEqual(t, len(remoteFS.GetErrors()), 200)
assert.Len(t, index.GetAllExternalIndexes(), 0)
}
func TestSpecIndex_k8s(t *testing.T) {
@@ -494,13 +564,9 @@ func test_buildMixedRefServer() *httptest.Server {
bs, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if req.URL.String() == "/daveshanley/vacuum/main/model/test_files/burgershop.openapi.yaml" {
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT")
_, _ = rw.Write(bs)
return
}
_, _ = rw.Write([]byte(`OK`))
}))
}
@@ -516,6 +582,9 @@ func TestSpecIndex_BurgerShopMixedRef(t *testing.T) {
cf.AllowRemoteLookup = true
cf.AvoidCircularReferenceCheck = true
cf.BasePath = "../test_specs"
cf.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelError,
}))
// setting this baseURL will override the base
cf.BaseURL, _ = url.Parse(server.URL)
@@ -720,7 +789,7 @@ func TestSpecIndex_FindComponent_WithACrazyAssPath(t *testing.T) {
index.FindComponent("#/paths/~1crazy~1ass~1references/get/parameters/0", nil).Node.Content[1].Value)
}
func TestSpecIndex_FindComponenth(t *testing.T) {
func TestSpecIndex_FindComponent(t *testing.T) {
yml := `components:
schemas:
pizza:
@@ -754,34 +823,34 @@ func TestSpecIndex_TestPathsNodeAsArray(t *testing.T) {
assert.Nil(t, index.lookupRolodex(nil))
}
func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_Error(t *testing.T) {
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{}
_, _, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/$.....#[;]something")
assert.Error(t, err)
}
//func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_Error(t *testing.T) {
// index := new(SpecIndex)
// index.seenRemoteSources = make(map[string]*yaml.Node)
// index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{}
// _, _, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/$.....#[;]something")
// assert.Error(t, err)
//}
func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadFind(t *testing.T) {
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{}
a, b, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/hey")
assert.Error(t, err)
assert.Nil(t, a)
assert.Nil(t, b)
}
//func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadFind(t *testing.T) {
// index := new(SpecIndex)
// index.seenRemoteSources = make(map[string]*yaml.Node)
// index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{}
// a, b, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/hey")
// assert.Error(t, err)
// assert.Nil(t, a)
// assert.Nil(t, b)
//}
// Discovered in issue https://github.com/pb33f/libopenapi/issues/37
func TestSpecIndex_lookupRemoteReference_NoComponent(t *testing.T) {
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["https://api.rest.sh/schemas/ErrorModel.json"] = &yaml.Node{}
a, b, err := index.lookupRemoteReference("https://api.rest.sh/schemas/ErrorModel.json")
assert.NoError(t, err)
assert.NotNil(t, a)
assert.NotNil(t, b)
}
//func TestSpecIndex_lookupRemoteReference_NoComponent(t *testing.T) {
// index := new(SpecIndex)
// index.seenRemoteSources = make(map[string]*yaml.Node)
// index.seenRemoteSources["https://api.rest.sh/schemas/ErrorModel.json"] = &yaml.Node{}
// a, b, err := index.lookupRemoteReference("https://api.rest.sh/schemas/ErrorModel.json")
// assert.NoError(t, err)
// assert.NotNil(t, a)
// assert.NotNil(t, b)
//}
// Discovered in issue https://github.com/daveshanley/vacuum/issues/225
func TestSpecIndex_lookupFileReference_NoComponent(t *testing.T) {
@@ -792,29 +861,13 @@ func TestSpecIndex_lookupFileReference_NoComponent(t *testing.T) {
_ = os.WriteFile("coffee-time.yaml", []byte("time: for coffee"), 0o664)
defer os.Remove("coffee-time.yaml")
index.seenRemoteSources = make(map[string]*yaml.Node)
//index.seenRemoteSources = make(map[string]*yaml.Node)
a, b, err := index.lookupFileReference("coffee-time.yaml")
assert.NoError(t, err)
assert.NotNil(t, a)
assert.NotNil(t, b)
}
func TestSpecIndex_CheckBadURLRef(t *testing.T) {
yml := `openapi: 3.1.0
paths:
/cakes:
post:
parameters:
- $ref: 'httpsss://badurl'`
var rootNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &rootNode)
index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig())
assert.Len(t, index.refErrors, 2)
}
func TestSpecIndex_CheckBadURLRefNoRemoteAllowed(t *testing.T) {
yml := `openapi: 3.1.0
paths:
@@ -829,15 +882,34 @@ paths:
c := CreateClosedAPIIndexConfig()
idx := NewSpecIndexWithConfig(&rootNode, c)
assert.Len(t, idx.refErrors, 2)
assert.Equal(t, "remote lookups are not permitted, "+
"please set AllowRemoteLookup to true in the configuration", idx.refErrors[0].Error())
assert.Len(t, idx.refErrors, 1)
}
func TestSpecIndex_CheckIndexDiscoversNoComponentLocalFileReference(t *testing.T) {
_ = os.WriteFile("coffee-time.yaml", []byte("name: time for coffee"), 0o664)
c := []byte("name: time for coffee")
_ = os.WriteFile("coffee-time.yaml", c, 0o664)
defer os.Remove("coffee-time.yaml")
// create a new config that allows local and remote to be mixed up.
cf := CreateOpenAPIIndexConfig()
cf.AvoidCircularReferenceCheck = true
cf.BasePath = "."
// create a new rolodex
rolo := NewRolodex(cf)
// configure the local filesystem.
fsCfg := LocalFSConfig{
BaseDirectory: cf.BasePath,
FileFilters: []string{"coffee-time.yaml"},
DirFS: os.DirFS(cf.BasePath),
}
// create a new local filesystem.
fileFS, err := NewLocalFSWithConfig(&fsCfg)
assert.NoError(t, err)
yml := `openapi: 3.0.3
paths:
/cakes:
@@ -845,24 +917,32 @@ paths:
parameters:
- $ref: 'coffee-time.yaml'`
var rootNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &rootNode)
var coffee yaml.Node
_ = yaml.Unmarshal([]byte(yml), &coffee)
index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig())
// set the rolodex root node to the root node of the spec.
rolo.SetRootNode(&coffee)
rolo.AddLocalFS(cf.BasePath, fileFS)
rErr := rolo.IndexTheRolodex()
assert.NoError(t, rErr)
index := rolo.GetRootIndex()
assert.NotNil(t, index.GetAllParametersFromOperations()["/cakes"]["post"]["coffee-time.yaml"][0].Node)
}
func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadJSON(t *testing.T) {
index := NewSpecIndexWithConfig(nil, &SpecIndexConfig{
//AllowRemoteLookup: true,
})
index.seenRemoteSources = make(map[string]*yaml.Node)
a, b, err := index.lookupRemoteReference("https://google.com//logos/doodles/2022/labor-day-2022-6753651837109490.3-l.png#/hey")
assert.Error(t, err)
assert.Nil(t, a)
assert.Nil(t, b)
}
//func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadJSON(t *testing.T) {
// index := NewSpecIndexWithConfig(nil, &SpecIndexConfig{
// //AllowRemoteLookup: true,
// })
// index.seenRemoteSources = make(map[string]*yaml.Node)
// a, b, err := index.lookupRemoteReference("https://google.com//logos/doodles/2022/labor-day-2022-6753651837109490.3-l.png#/hey")
// assert.Error(t, err)
// assert.Nil(t, a)
// assert.Nil(t, b)
//}
func TestSpecIndex_lookupFileReference_BadFileName(t *testing.T) {
index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig())
@@ -870,39 +950,79 @@ func TestSpecIndex_lookupFileReference_BadFileName(t *testing.T) {
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_SeenSourceSimulation_Error(t *testing.T) {
index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig())
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["magic-money-file.json"] = &yaml.Node{}
_, _, err := index.lookupFileReference("magic-money-file.json#something")
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_BadFile(t *testing.T) {
index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig())
_, _, err := index.lookupFileReference("chickers.json#no-rice")
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_BadFileDataRead(t *testing.T) {
_ = os.WriteFile("chickers.yaml", []byte("broke: the: thing: [again]"), 0o664)
defer os.Remove("chickers.yaml")
var root yaml.Node
index := NewSpecIndexWithConfig(&root, CreateOpenAPIIndexConfig())
_, _, err := index.lookupFileReference("chickers.yaml#no-rice")
assert.Error(t, err)
}
//
//func TestSpecIndex_lookupFileReference_SeenSourceSimulation_Error(t *testing.T) {
// index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig())
// index.seenRemoteSources = make(map[string]*yaml.Node)
// index.seenRemoteSources["magic-money-file.json"] = &yaml.Node{}
// _, _, err := index.lookupFileReference("magic-money-file.json#something")
// assert.Error(t, err)
//}
//
//func TestSpecIndex_lookupFileReference_BadFile(t *testing.T) {
// index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig())
// _, _, err := index.lookupFileReference("chickers.json#no-rice")
// assert.Error(t, err)
//}
//
//func TestSpecIndex_lookupFileReference_BadFileDataRead(t *testing.T) {
// _ = os.WriteFile("chickers.yaml", []byte("broke: the: thing: [again]"), 0o664)
// defer os.Remove("chickers.yaml")
// var root yaml.Node
// index := NewSpecIndexWithConfig(&root, CreateOpenAPIIndexConfig())
// _, _, err := index.lookupFileReference("chickers.yaml#no-rice")
// assert.Error(t, err)
//}
func TestSpecIndex_lookupFileReference_MultiRes(t *testing.T) {
_ = os.WriteFile("embie.yaml", []byte("naughty:\n - puppy: dog\n - puppy: naughty\npuppy:\n - naughty: puppy"), 0o664)
embie := []byte("naughty:\n - puppy: dog\n - puppy: naughty\npuppy:\n - naughty: puppy")
_ = os.WriteFile("embie.yaml", embie, 0o664)
defer os.Remove("embie.yaml")
index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig())
index.seenRemoteSources = make(map[string]*yaml.Node)
k, doc, err := index.lookupFileReference("embie.yaml#/.naughty")
// create a new config that allows local and remote to be mixed up.
cf := CreateOpenAPIIndexConfig()
cf.AvoidBuildIndex = true
cf.AvoidCircularReferenceCheck = true
cf.BasePath = "."
// create a new rolodex
rolo := NewRolodex(cf)
var myPuppy yaml.Node
_ = yaml.Unmarshal(embie, &myPuppy)
// set the rolodex root node to the root node of the spec.
rolo.SetRootNode(&myPuppy)
// configure the local filesystem.
fsCfg := LocalFSConfig{
BaseDirectory: cf.BasePath,
FileFilters: []string{"embie.yaml"},
DirFS: os.DirFS(cf.BasePath),
}
// create a new local filesystem.
fileFS, err := NewLocalFSWithConfig(&fsCfg)
assert.NoError(t, err)
assert.NotNil(t, doc)
assert.Nil(t, k)
rolo.AddLocalFS(cf.BasePath, fileFS)
rErr := rolo.IndexTheRolodex()
assert.NoError(t, rErr)
embieRoloFile, fErr := rolo.Open("embie.yaml")
assert.NoError(t, fErr)
assert.NotNil(t, embieRoloFile)
index := rolo.GetRootIndex()
//index.seenRemoteSources = make(map[string]*yaml.Node)
absoluteRef, _ := filepath.Abs("embie.yaml#/naughty")
fRef := index.SearchIndexForReference(absoluteRef)
assert.NotNil(t, fRef)
}
func TestSpecIndex_lookupFileReference(t *testing.T) {
@@ -918,7 +1038,6 @@ func TestSpecIndex_lookupFileReference(t *testing.T) {
// create a new config that allows local and remote to be mixed up.
cf := CreateOpenAPIIndexConfig()
cf.AvoidBuildIndex = true
cf.AllowRemoteLookup = true
cf.AvoidCircularReferenceCheck = true
cf.BasePath = "."