2024-12-18 10:19:14 +00:00
package main
import (
"bytes"
"io"
"os"
"path"
"reflect"
"strings"
"testing"
"github.com/databricks/cli/bundle/config"
"github.com/databricks/cli/libs/dyn"
"github.com/databricks/cli/libs/dyn/merge"
"github.com/databricks/cli/libs/dyn/yamlloader"
"github.com/databricks/cli/libs/jsonschema"
"github.com/stretchr/testify/assert"
2024-12-19 08:23:05 +00:00
"gopkg.in/yaml.v3"
2024-12-18 10:19:14 +00:00
)
func copyFile ( src , dst string ) error {
in , err := os . Open ( src )
if err != nil {
return err
}
defer in . Close ( )
out , err := os . Create ( dst )
if err != nil {
return err
}
defer out . Close ( )
_ , err = io . Copy ( out , in )
if err != nil {
return err
}
return out . Close ( )
}
// Checks whether descriptions are added for new config fields in the annotations.yml file
// If this test fails either manually add descriptions to the `annotations.yml` or do the following:
2024-12-23 12:08:01 +00:00
// 1. for fields described outside of CLI package fetch latest schema from the OpenAPI spec and add path to file to DATABRICKS_OPENAPI_SPEC env variable
// 2. run `make schema` from the repository root to add placeholder descriptions
2024-12-18 10:19:14 +00:00
// 2. replace all "PLACEHOLDER" values with the actual descriptions if possible
// 3. run `make schema` again to regenerate the schema with acutal descriptions
func TestRequiredAnnotationsForNewFields ( t * testing . T ) {
workdir := t . TempDir ( )
annotationsPath := path . Join ( workdir , "annotations.yml" )
annotationsOpenApiPath := path . Join ( workdir , "annotations_openapi.yml" )
annotationsOpenApiOverridesPath := path . Join ( workdir , "annotations_openapi_overrides.yml" )
// Copy existing annotation files from the same folder as this test
err := copyFile ( "annotations.yml" , annotationsPath )
assert . NoError ( t , err )
err = copyFile ( "annotations_openapi.yml" , annotationsOpenApiPath )
assert . NoError ( t , err )
err = copyFile ( "annotations_openapi_overrides.yml" , annotationsOpenApiOverridesPath )
assert . NoError ( t , err )
generateSchema ( workdir , path . Join ( t . TempDir ( ) , "schema.json" ) )
originalFile , err := os . ReadFile ( "annotations.yml" )
assert . NoError ( t , err )
currentFile , err := os . ReadFile ( annotationsPath )
assert . NoError ( t , err )
original , err := yamlloader . LoadYAML ( "" , bytes . NewBuffer ( originalFile ) )
assert . NoError ( t , err )
current , err := yamlloader . LoadYAML ( "" , bytes . NewBuffer ( currentFile ) )
assert . NoError ( t , err )
// Collect added paths.
var updatedFieldPaths [ ] string
_ , err = merge . Override ( original , current , merge . OverrideVisitor {
VisitInsert : func ( basePath dyn . Path , right dyn . Value ) ( dyn . Value , error ) {
updatedFieldPaths = append ( updatedFieldPaths , basePath . String ( ) )
return right , nil
} ,
} )
assert . NoError ( t , err )
2025-01-02 11:03:41 +00:00
assert . Empty ( t , updatedFieldPaths , "Missing JSON-schema descriptions for new config fields in bundle/internal/schema/annotations.yml:\n%s" , strings . Join ( updatedFieldPaths , "\n" ) )
2024-12-18 10:19:14 +00:00
}
// Checks whether types in annotation files are still present in Config type
func TestNoDetachedAnnotations ( t * testing . T ) {
files := [ ] string {
"annotations.yml" ,
"annotations_openapi.yml" ,
"annotations_openapi_overrides.yml" ,
}
types := map [ string ] bool { }
for _ , file := range files {
annotations , err := getAnnotations ( file )
assert . NoError ( t , err )
for k := range annotations {
types [ k ] = false
}
}
_ , err := jsonschema . FromType ( reflect . TypeOf ( config . Root { } ) , [ ] func ( reflect . Type , jsonschema . Schema ) jsonschema . Schema {
func ( typ reflect . Type , s jsonschema . Schema ) jsonschema . Schema {
delete ( types , getPath ( typ ) )
return s
} ,
} )
assert . NoError ( t , err )
for typ := range types {
t . Errorf ( "Type `%s` in annotations file is not found in `root.Config` type" , typ )
}
assert . Empty ( t , types , "Detached annotations found, regenerate schema and check for package path changes" )
}
func getAnnotations ( path string ) ( annotationFile , error ) {
b , err := os . ReadFile ( path )
if err != nil {
return nil , err
}
var data annotationFile
err = yaml . Unmarshal ( b , & data )
return data , err
}