diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 43614126..77d61741 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -13,18 +13,26 @@ jobs: name: Build Windows runs-on: windows-latest steps: + - name: Force git to use LF + # This step is required on Windows to work around issues with go fmt. + # TODO: replace with a checkout option when https://github.com/actions/checkout/issues/226 is implemented + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - name: Checkout code + uses: actions/checkout@v6 + - name: Set up Go 1.x - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 1.23 + go-version-file: go.mod id: go - - name: Checkout code - uses: actions/checkout@v3 - - - name: Get dependencies + - name: Format run: | - go get -v -t -d ./... + go fmt ./... + git diff --exit-code - name: Test run: go test ./... build: @@ -32,22 +40,19 @@ jobs: runs-on: ubuntu-latest steps: + - name: Checkout code + uses: actions/checkout@v6 + - name: Set up Go 1.x - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 1.23 + go-version-file: go.mod id: go - - name: Checkout code - uses: actions/checkout@v3 - - - name: Get dependencies + - name: Format run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi + go fmt ./... + git diff --exit-code - name: Test run: go test ./... - name: Coverage diff --git a/bundler/bundler_test.go b/bundler/bundler_test.go index 7de789af..0af5a77e 100644 --- a/bundler/bundler_test.go +++ b/bundler/bundler_test.go @@ -2319,8 +2319,8 @@ func TestCopySchemaToComponents_NameCollision(t *testing.T) { func TestCalculateCollisionNameInline_NumericSuffix(t *testing.T) { // Test: When filename-based name also collides, use numeric suffix existingNames := map[string]bool{ - "Cat": true, - "Cat__external": true, // Filename-based collision also exists + "Cat": true, + "Cat__external": true, // Filename-based collision also exists "Cat__external__1": true, // First numeric suffix also taken (format: name__basename__N) } diff --git a/bundler/origin_test.go b/bundler/origin_test.go index 69c9eb0a..8151af4e 100644 --- a/bundler/origin_test.go +++ b/bundler/origin_test.go @@ -914,7 +914,7 @@ func TestCaptureOrigin_FullCoverage(t *testing.T) { pr := &processRef{ ref: &index.Reference{ FullDefinition: "test.yaml", - Node: &yaml.Node{Line: 5, Column: 2}, + Node: &yaml.Node{Line: 5, Column: 2}, }, idx: &index.SpecIndex{}, originalName: "Test", diff --git a/datamodel/high/base/dynamic_value.go b/datamodel/high/base/dynamic_value.go index 9004d9d2..40473b65 100644 --- a/datamodel/high/base/dynamic_value.go +++ b/datamodel/high/base/dynamic_value.go @@ -22,11 +22,11 @@ import ( // // The N value indicates which value is set (0 = A, 1 == B), preventing the need to check both values. type DynamicValue[A any, B any] struct { - N int // 0 == A, 1 == B - A A - B B - inline bool - renderCtx any // Context for inline rendering (typed as any to avoid import cycles) + N int // 0 == A, 1 == B + A A + B B + inline bool + renderCtx any // Context for inline rendering (typed as any to avoid import cycles) } // IsA will return true if the 'A' or left value is set. diff --git a/datamodel/high/overlay/action.go b/datamodel/high/overlay/action.go index c1ad4037..86b0dd3c 100644 --- a/datamodel/high/overlay/action.go +++ b/datamodel/high/overlay/action.go @@ -13,11 +13,11 @@ import ( // Action represents a high-level Overlay Action Object. // https://spec.openapis.org/overlay/v1.1.0#action-object type Action struct { - Target string `json:"target,omitempty" yaml:"target,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - Update *yaml.Node `json:"update,omitempty" yaml:"update,omitempty"` - Remove bool `json:"remove,omitempty" yaml:"remove,omitempty"` - Copy string `json:"copy,omitempty" yaml:"copy,omitempty"` + Target string `json:"target,omitempty" yaml:"target,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Update *yaml.Node `json:"update,omitempty" yaml:"update,omitempty"` + Remove bool `json:"remove,omitempty" yaml:"remove,omitempty"` + Copy string `json:"copy,omitempty" yaml:"copy,omitempty"` Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` low *low.Action } diff --git a/datamodel/high/overlay/info.go b/datamodel/high/overlay/info.go index b4dfcd9b..dc3eeb5f 100644 --- a/datamodel/high/overlay/info.go +++ b/datamodel/high/overlay/info.go @@ -13,9 +13,9 @@ import ( // Info represents a high-level Overlay Info Object. // https://spec.openapis.org/overlay/v1.1.0#info-object type Info struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` low *low.Info } diff --git a/datamodel/high/overlay/overlay.go b/datamodel/high/overlay/overlay.go index 64786179..7a6288a8 100644 --- a/datamodel/high/overlay/overlay.go +++ b/datamodel/high/overlay/overlay.go @@ -13,10 +13,10 @@ import ( // Overlay represents a high-level OpenAPI Overlay document. // https://spec.openapis.org/overlay/v1.0.0 type Overlay struct { - Overlay string `json:"overlay,omitempty" yaml:"overlay,omitempty"` - Info *Info `json:"info,omitempty" yaml:"info,omitempty"` - Extends string `json:"extends,omitempty" yaml:"extends,omitempty"` - Actions []*Action `json:"actions,omitempty" yaml:"actions,omitempty"` + Overlay string `json:"overlay,omitempty" yaml:"overlay,omitempty"` + Info *Info `json:"info,omitempty" yaml:"info,omitempty"` + Extends string `json:"extends,omitempty" yaml:"extends,omitempty"` + Actions []*Action `json:"actions,omitempty" yaml:"actions,omitempty"` Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"` low *low.Overlay } diff --git a/datamodel/high/v3/request_body_test.go b/datamodel/high/v3/request_body_test.go index 81abd4bf..6a073f04 100644 --- a/datamodel/high/v3/request_body_test.go +++ b/datamodel/high/v3/request_body_test.go @@ -320,4 +320,3 @@ paths: {}` assert.NoError(t, err) assert.NotNil(t, result) } - diff --git a/datamodel/high/v3/security_scheme_test.go b/datamodel/high/v3/security_scheme_test.go index 1ffbc27f..c9365f7d 100644 --- a/datamodel/high/v3/security_scheme_test.go +++ b/datamodel/high/v3/security_scheme_test.go @@ -157,7 +157,6 @@ func TestSecurityScheme_MarshalYAMLInlineWithContext_Reference(t *testing.T) { assert.Equal(t, "$ref", yamlNode.Content[0].Value) } - func TestBuildLowSecurityScheme_Success(t *testing.T) { yml := `type: apiKey name: X-API-Key diff --git a/datamodel/high/v3/server.go b/datamodel/high/v3/server.go index 2b2b9b44..4f6a3cb0 100644 --- a/datamodel/high/v3/server.go +++ b/datamodel/high/v3/server.go @@ -14,7 +14,7 @@ import ( // Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one. // - https://spec.openapis.org/oas/v3.1.0#server-object type Server struct { - Name string `json:"name,omitempty" yaml:"name,omitempty"` // OpenAPI 3.2+ name field for documentation + Name string `json:"name,omitempty" yaml:"name,omitempty"` // OpenAPI 3.2+ name field for documentation URL string `json:"url,omitempty" yaml:"url,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Variables *orderedmap.Map[string, *ServerVariable] `json:"variables,omitempty" yaml:"variables,omitempty"` diff --git a/datamodel/low/v3/security_scheme.go b/datamodel/low/v3/security_scheme.go index fb760281..42788eed 100644 --- a/datamodel/low/v3/security_scheme.go +++ b/datamodel/low/v3/security_scheme.go @@ -25,21 +25,21 @@ import ( // Recommended for most use case is Authorization Code Grant flow with PKCE. // - https://spec.openapis.org/oas/v3.1.0#security-scheme-object type SecurityScheme struct { - Type low.NodeReference[string] - Description low.NodeReference[string] - Name low.NodeReference[string] - In low.NodeReference[string] - Scheme low.NodeReference[string] - BearerFormat low.NodeReference[string] - Flows low.NodeReference[*OAuthFlows] - OpenIdConnectUrl low.NodeReference[string] + Type low.NodeReference[string] + Description low.NodeReference[string] + Name low.NodeReference[string] + In low.NodeReference[string] + Scheme low.NodeReference[string] + BearerFormat low.NodeReference[string] + Flows low.NodeReference[*OAuthFlows] + OpenIdConnectUrl low.NodeReference[string] OAuth2MetadataUrl low.NodeReference[string] // OpenAPI 3.2+ OAuth2 metadata URL - Deprecated low.NodeReference[bool] // OpenAPI 3.2+ deprecated flag - Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] - KeyNode *yaml.Node - RootNode *yaml.Node - index *index.SpecIndex - context context.Context + Deprecated low.NodeReference[bool] // OpenAPI 3.2+ deprecated flag + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + index *index.SpecIndex + context context.Context *low.Reference low.NodeMap } diff --git a/datamodel/spec_info.go b/datamodel/spec_info.go index 0154df80..caec7284 100644 --- a/datamodel/spec_info.go +++ b/datamodel/spec_info.go @@ -37,7 +37,7 @@ type SpecInfo struct { APISchema string `json:"-"` // API Schema for supplied spec type (2 or 3) Generated time.Time `json:"-"` OriginalIndentation int `json:"-"` // the original whitespace - Self string `json:"-"` // the $self field for OpenAPI 3.2+ documents (base URI) + Self string `json:"-"` // the $self field for OpenAPI 3.2+ documents (base URI) } func ExtractSpecInfoWithConfig(spec []byte, config *DocumentConfiguration) (*SpecInfo, error) { diff --git a/datamodel/translate_coverage_test.go b/datamodel/translate_coverage_test.go index 07972095..86bae175 100644 --- a/datamodel/translate_coverage_test.go +++ b/datamodel/translate_coverage_test.go @@ -16,13 +16,15 @@ import ( // TestTranslateMapParallel_ContextCancellation specifically targets lines 158-159 // in translate.go which handle context cancellation during job dispatch. // This test ensures 100% coverage even on single-CPU systems like GitHub runners. -// +// // The flaky coverage issue occurs because the select statement at lines 156-160: -// select { -// case jobChan <- j: -// case <-ctx.Done(): -// return -// } +// +// select { +// case jobChan <- j: +// case <-ctx.Done(): +// return +// } +// // The ctx.Done() branch (lines 158-159) is only hit when the context is cancelled // while the goroutine is blocked trying to send to jobChan. This is a race condition // that doesn't always occur, especially on single-CPU systems. @@ -57,7 +59,7 @@ func TestTranslateMapParallel_ContextCancellation(t *testing.T) { time.Sleep(10 * time.Millisecond) return "", errors.New("trigger cancellation") } - + // Other jobs: count how many get started jobsBlocked.Add(1) time.Sleep(100 * time.Millisecond) @@ -72,13 +74,13 @@ func TestTranslateMapParallel_ContextCancellation(t *testing.T) { err := datamodel.TranslateMapParallel[string, int, string](m, translateFunc, resultFunc) require.Error(t, err) assert.Contains(t, err.Error(), "trigger cancellation") - + // Wait for goroutines to clean up time.Sleep(20 * time.Millisecond) - + // Verify context cancellation prevented all jobs from running // If lines 158-159 are hit, some jobs will be skipped - assert.Less(t, int(jobsBlocked.Load()), itemCount-1, + assert.Less(t, int(jobsBlocked.Load()), itemCount-1, "Iteration %d: Context cancellation should prevent some jobs", iteration) } -} \ No newline at end of file +} diff --git a/index/enhanced_coverage_test.go b/index/enhanced_coverage_test.go index e23cc9dc..88f624c1 100644 --- a/index/enhanced_coverage_test.go +++ b/index/enhanced_coverage_test.go @@ -193,4 +193,4 @@ other: test`, } } }) -} \ No newline at end of file +} diff --git a/index/issue361_test.go b/index/issue361_test.go index 6d6deed1..20cd1ca5 100644 --- a/index/issue361_test.go +++ b/index/issue361_test.go @@ -48,7 +48,7 @@ properties: // Create a Rolodex and add the standard fs.FS config := CreateOpenAPIIndexConfig() rolo := NewRolodex(config) - + // Add the fs.FS with a base directory // The fix ensures that when opening files, relative paths are used // with the fs.FS interface, not absolute paths @@ -56,20 +56,20 @@ properties: tempDir, err := os.MkdirTemp("", "rolodex-test") require.NoError(t, err, "Should be able to create temp dir") defer os.RemoveAll(tempDir) - + baseDir := filepath.Join(tempDir, "api", "v1") rolo.AddLocalFS(baseDir, testFS) - + // Test 1: Open a file at the root of the FS f1, err := rolo.Open("openapi.yaml") require.NoError(t, err, "Should open file using relative path with fs.FS") assert.Contains(t, f1.GetContent(), "Test API") - + // Test 2: Open a nested file f2, err := rolo.Open("schemas/pet.yaml") require.NoError(t, err, "Should open nested file using relative path with fs.FS") assert.Contains(t, f2.GetContent(), "type: object") - + // Test 3: Verify absolute paths are converted correctly // Even if we pass an absolute path matching the base + relative path, // it should work by converting to relative @@ -87,33 +87,33 @@ func TestIssue361_MultipleFileSystems(t *testing.T) { apiFS := fstest.MapFS{ "api.yaml": {Data: []byte("api content"), ModTime: time.Now()}, } - + schemaFS := fstest.MapFS{ "schema.json": {Data: []byte("schema content"), ModTime: time.Now()}, } - + // Create Rolodex with multiple file systems config := CreateOpenAPIIndexConfig() rolo := NewRolodex(config) - + // Use temporary directories for cross-platform compatibility tempDir, err := os.MkdirTemp("", "rolodex-multi-test") require.NoError(t, err, "Should be able to create temp dir") defer os.RemoveAll(tempDir) - + rolo.AddLocalFS(filepath.Join(tempDir, "apis"), apiFS) rolo.AddLocalFS(filepath.Join(tempDir, "schemas"), schemaFS) - + // Files should be found in their respective file systems f1, err := rolo.Open("api.yaml") require.NoError(t, err, "Should find api.yaml in first FS") assert.Equal(t, "api content", f1.GetContent()) - + f2, err := rolo.Open("schema.json") require.NoError(t, err, "Should find schema.json in second FS") assert.Equal(t, "schema content", f2.GetContent()) - + // Non-existent file should return error _, err = rolo.Open("nonexistent.yaml") assert.Error(t, err, "Should return error for non-existent file") -} \ No newline at end of file +} diff --git a/index/rolodex_fscompat_test.go b/index/rolodex_fscompat_test.go index 9f9442d8..1dd37dc1 100644 --- a/index/rolodex_fscompat_test.go +++ b/index/rolodex_fscompat_test.go @@ -34,18 +34,18 @@ func (s strictFS) Open(name string) (fs.File, error) { func TestRolodex_FSCompatibility_RelativePath(t *testing.T) { t.Parallel() - + // Create a test filesystem that strictly enforces fs.FS interface testFS := strictFS{ FS: fstest.MapFS{ - "spec.yaml": {Data: []byte("test content"), ModTime: time.Now()}, - "refs/common.yaml": {Data: []byte("common ref"), ModTime: time.Now()}, - "schemas/pet.yaml": {Data: []byte("pet schema"), ModTime: time.Now()}, + "spec.yaml": {Data: []byte("test content"), ModTime: time.Now()}, + "refs/common.yaml": {Data: []byte("common ref"), ModTime: time.Now()}, + "schemas/pet.yaml": {Data: []byte("pet schema"), ModTime: time.Now()}, }, } baseDir := "/project/api" - + rolo := NewRolodex(CreateOpenAPIIndexConfig()) rolo.AddLocalFS(baseDir, testFS) @@ -67,17 +67,17 @@ func TestRolodex_FSCompatibility_RelativePath(t *testing.T) { func TestRolodex_FSCompatibility_AbsolutePath(t *testing.T) { t.Parallel() - + // Create a test filesystem that strictly enforces fs.FS interface testFS := strictFS{ FS: fstest.MapFS{ - "api/spec.yaml": {Data: []byte("api spec"), ModTime: time.Now()}, - "common/base.yaml": {Data: []byte("base spec"), ModTime: time.Now()}, + "api/spec.yaml": {Data: []byte("api spec"), ModTime: time.Now()}, + "common/base.yaml": {Data: []byte("base spec"), ModTime: time.Now()}, }, } baseDir, _ := filepath.Abs("/tmp/test") - + rolo := NewRolodex(CreateOpenAPIIndexConfig()) rolo.AddLocalFS(baseDir, testFS) @@ -90,17 +90,17 @@ func TestRolodex_FSCompatibility_AbsolutePath(t *testing.T) { func TestRolodex_FSCompatibility_MultipleFS(t *testing.T) { t.Parallel() - + // For this test, we don't need strict enforcement since we're testing // the ability to find files across multiple file systems // The strict enforcement is tested in other test cases apiFS := fstest.MapFS{ "openapi.yaml": {Data: []byte("api spec"), ModTime: time.Now()}, } - + schemasFS := fstest.MapFS{ - "pet.json": {Data: []byte("pet schema"), ModTime: time.Now()}, - "store.json": {Data: []byte("store schema"), ModTime: time.Now()}, + "pet.json": {Data: []byte("pet schema"), ModTime: time.Now()}, + "store.json": {Data: []byte("store schema"), ModTime: time.Now()}, } rolo := NewRolodex(CreateOpenAPIIndexConfig()) @@ -120,7 +120,7 @@ func TestRolodex_FSCompatibility_MultipleFS(t *testing.T) { func TestRolodex_FSCompatibility_StandardFS(t *testing.T) { t.Parallel() - + // Test with various standard fs.FS implementations testCases := []struct { name string @@ -139,10 +139,10 @@ func TestRolodex_FSCompatibility_StandardFS(t *testing.T) { t.Run(tc.name, func(t *testing.T) { rolo := NewRolodex(CreateOpenAPIIndexConfig()) rolo.AddLocalFS("/base", tc.fs) - + f, err := rolo.Open("test.yaml") require.NoError(t, err, "Should work with %s", tc.name) assert.Equal(t, "test data", f.GetContent()) }) } -} \ No newline at end of file +} diff --git a/index/schema_id_types.go b/index/schema_id_types.go index ea118eff..752b01fd 100644 --- a/index/schema_id_types.go +++ b/index/schema_id_types.go @@ -10,14 +10,14 @@ import ( // SchemaIdEntry represents a schema registered by its JSON Schema 2020-12 $id. // This enables $ref resolution against $id values per JSON Schema specification. type SchemaIdEntry struct { - Id string // The $id value as declared in the schema - ResolvedUri string // Fully resolved absolute URI after applying base URI resolution - SchemaNode *yaml.Node // The YAML node containing the schema with this $id - ParentId string // The $id of the parent scope (for nested schemas with $id) - Index *SpecIndex // Reference to the SpecIndex containing this schema - DefinitionPath string // JSON pointer path to this schema (e.g., #/components/schemas/Pet) - Line int // Line number where $id was declared (for error reporting) - Column int // Column number where $id was declared (for error reporting) + Id string // The $id value as declared in the schema + ResolvedUri string // Fully resolved absolute URI after applying base URI resolution + SchemaNode *yaml.Node // The YAML node containing the schema with this $id + ParentId string // The $id of the parent scope (for nested schemas with $id) + Index *SpecIndex // Reference to the SpecIndex containing this schema + DefinitionPath string // JSON pointer path to this schema (e.g., #/components/schemas/Pet) + Line int // Line number where $id was declared (for error reporting) + Column int // Column number where $id was declared (for error reporting) } // GetKey returns the registry key for this entry. diff --git a/index/utility_methods_test.go b/index/utility_methods_test.go index 57d65ac9..c6901078 100644 --- a/index/utility_methods_test.go +++ b/index/utility_methods_test.go @@ -320,7 +320,7 @@ func Test_WriteIntToHash(t *testing.T) { assert.NotZero(t, h.Sum64()) } - // verify consistency - hash should be same on repeated calls +// verify consistency - hash should be same on repeated calls func Test_Empty_HashNode(t *testing.T) { assert.Equal(t, emptyNodeHash, HashNode(nil)) } diff --git a/overlay/engine_test.go b/overlay/engine_test.go index ffaef2d2..3ec9d2e6 100644 --- a/overlay/engine_test.go +++ b/overlay/engine_test.go @@ -7,8 +7,8 @@ import ( "context" "testing" - "github.com/pb33f/libopenapi/datamodel/low" highoverlay "github.com/pb33f/libopenapi/datamodel/high/overlay" + "github.com/pb33f/libopenapi/datamodel/low" lowoverlay "github.com/pb33f/libopenapi/datamodel/low/overlay" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/overlay_test.go b/overlay_test.go index bc55cb2a..b5e0a60a 100644 --- a/overlay_test.go +++ b/overlay_test.go @@ -531,4 +531,3 @@ actions: assert.Error(t, err) assert.Nil(t, result) } - diff --git a/utils/utils.go b/utils/utils.go index e57d36e3..df651b7e 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -637,7 +637,7 @@ func GetRefValueNode(node *yaml.Node) *yaml.Node { for i, r := range n.Content { if i%2 == 0 && r.Value == "$ref" { if i+1 < len(n.Content) { - return n.Content[i + 1] + return n.Content[i+1] } } } diff --git a/utils/utils_bench_test.go b/utils/utils_bench_test.go index 280a5815..20b273f4 100644 --- a/utils/utils_bench_test.go +++ b/utils/utils_bench_test.go @@ -20,7 +20,7 @@ func BenchmarkPathCharExp_Regex(b *testing.B) { "123numeric", "back\\slash", } - + b.ResetTimer() for i := 0; i < b.N; i++ { for _, tc := range testCases { @@ -32,7 +32,7 @@ func BenchmarkPathCharExp_Regex(b *testing.B) { func BenchmarkPathCharExp_Optimized(b *testing.B) { testCases := []string{ "simple", - "SimpleCase", + "SimpleCase", "with_underscore", "with-dash", "with spaces", @@ -40,11 +40,11 @@ func BenchmarkPathCharExp_Optimized(b *testing.B) { "123numeric", "back\\slash", } - + b.ResetTimer() for i := 0; i < b.N; i++ { for _, tc := range testCases { _ = isPathChar(tc) } } -} \ No newline at end of file +} diff --git a/utils/utils_regex_bench_test.go b/utils/utils_regex_bench_test.go index ea0cc148..6eb574f0 100644 --- a/utils/utils_regex_bench_test.go +++ b/utils/utils_regex_bench_test.go @@ -9,7 +9,7 @@ import ( func BenchmarkRegexMatchString(b *testing.B) { re := regexp.MustCompile(`^[A-Za-z0-9_\\]*$`) testString := "simple_test_123" - + b.ResetTimer() for i := 0; i < b.N; i++ { _ = re.MatchString(testString) @@ -19,7 +19,7 @@ func BenchmarkRegexMatchString(b *testing.B) { // Optimized character check benchmark func BenchmarkOptimizedCharCheck(b *testing.B) { testString := "simple_test_123" - + b.ResetTimer() for i := 0; i < b.N; i++ { _ = isPathChar(testString) @@ -49,4 +49,4 @@ func BenchmarkConvertComponentPath_VeryComplex(b *testing.B) { for i := 0; i < b.N; i++ { _, _ = ConvertComponentIdIntoFriendlyPathSearch(path) } -} \ No newline at end of file +} diff --git a/what-changed/model/breaking_rules.go b/what-changed/model/breaking_rules.go index b0579e5c..642a07d2 100644 --- a/what-changed/model/breaking_rules.go +++ b/what-changed/model/breaking_rules.go @@ -333,9 +333,9 @@ func buildDefaultRules() *BreakingRulesConfig { Contains: rule(true, false, true), UnevaluatedItems: rule(true, false, true), UnevaluatedProperties: rule(true, true, true), - DynamicAnchor: rule(false, true, true), // $dynamicAnchor: modification/removal is breaking - DynamicRef: rule(false, true, true), // $dynamicRef: modification/removal is breaking - Id: rule(true, true, true), // $id: all changes are breaking (affects reference resolution) + DynamicAnchor: rule(false, true, true), // $dynamicAnchor: modification/removal is breaking + DynamicRef: rule(false, true, true), // $dynamicRef: modification/removal is breaking + Id: rule(true, true, true), // $id: all changes are breaking (affects reference resolution) Comment: rule(false, false, false), // $comment: does not affect API contracts ContentSchema: rule(true, true, true), // contentSchema: affects content validation Vocabulary: rule(true, true, true), // $vocabulary: affects schema interpretation @@ -400,9 +400,9 @@ func buildDefaultRules() *BreakingRulesConfig { OAuth2MetadataUrl: rule(false, false, false), Flows: rule(false, false, true), Scopes: rule(false, false, true), - Flow: rule(true, true, true), // Swagger 2.0 - AuthorizationURL: rule(true, true, true), // Swagger 2.0 - TokenURL: rule(true, true, true), // Swagger 2.0 + Flow: rule(true, true, true), // Swagger 2.0 + AuthorizationURL: rule(true, true, true), // Swagger 2.0 + TokenURL: rule(true, true, true), // Swagger 2.0 Deprecated: rule(false, false, false), }, diff --git a/what-changed/model/breaking_rules_model.go b/what-changed/model/breaking_rules_model.go index 696edb6d..89cc2bf6 100644 --- a/what-changed/model/breaking_rules_model.go +++ b/what-changed/model/breaking_rules_model.go @@ -260,9 +260,9 @@ type SecuritySchemeRules struct { OAuth2MetadataUrl *BreakingChangeRule `json:"oauth2MetadataUrl,omitempty" yaml:"oauth2MetadataUrl,omitempty"` Flows *BreakingChangeRule `json:"flows,omitempty" yaml:"flows,omitempty"` Scopes *BreakingChangeRule `json:"scopes,omitempty" yaml:"scopes,omitempty"` - Flow *BreakingChangeRule `json:"flow,omitempty" yaml:"flow,omitempty"` // Swagger 2.0 + Flow *BreakingChangeRule `json:"flow,omitempty" yaml:"flow,omitempty"` // Swagger 2.0 AuthorizationURL *BreakingChangeRule `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` // Swagger 2.0 - TokenURL *BreakingChangeRule `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` // Swagger 2.0 + TokenURL *BreakingChangeRule `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` // Swagger 2.0 Deprecated *BreakingChangeRule `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` } diff --git a/what-changed/model/document.go b/what-changed/model/document.go index 4d6c145e..5c9263c1 100644 --- a/what-changed/model/document.go +++ b/what-changed/model/document.go @@ -152,7 +152,7 @@ func CompareDocuments(l, r any) *DocumentChanges { // reset schema hashmap base.SchemaQuickHashMap.Clear() - + // clear hash cache to ensure clean state for comparison low.ClearHashCache() diff --git a/what-changed/model/encoding_test.go b/what-changed/model/encoding_test.go index 57448a4e..ae36ee23 100644 --- a/what-changed/model/encoding_test.go +++ b/what-changed/model/encoding_test.go @@ -118,7 +118,7 @@ allowReserved: true` // compare. extChanges := CompareEncoding(&lDoc, &rDoc) assert.NotNil(t, extChanges) - assert.Equal(t, 2, extChanges.TotalChanges()) // style added + header added + assert.Equal(t, 2, extChanges.TotalChanges()) // style added + header added assert.Len(t, extChanges.GetAllChanges(), 2) assert.Equal(t, 1, extChanges.TotalBreakingChanges()) // style added is breaking } @@ -153,7 +153,7 @@ allowReserved: true` // compare. extChanges := CompareEncoding(&rDoc, &lDoc) assert.NotNil(t, extChanges) - assert.Equal(t, 2, extChanges.TotalChanges()) // style removed + header removed + assert.Equal(t, 2, extChanges.TotalChanges()) // style removed + header removed assert.Len(t, extChanges.GetAllChanges(), 2) assert.Equal(t, 2, extChanges.TotalBreakingChanges()) // both style and header removed are breaking } diff --git a/what-changed/model/nil_checks_test.go b/what-changed/model/nil_checks_test.go index 4cf95fd8..687ee1d5 100644 --- a/what-changed/model/nil_checks_test.go +++ b/what-changed/model/nil_checks_test.go @@ -190,9 +190,9 @@ func TestAllChangesModels_NilChecks(t *testing.T) { {"InfoChanges_TotalBreakingChanges_WithNestedChanges", func(t *testing.T) { breakingChange := &Change{Breaking: true} i := &InfoChanges{ - PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}, - ContactChanges: &ContactChanges{PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}}, - LicenseChanges: &LicenseChanges{PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}}, + PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}, + ContactChanges: &ContactChanges{PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}}, + LicenseChanges: &LicenseChanges{PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}}, ExtensionChanges: &ExtensionChanges{PropertyChanges: &PropertyChanges{Changes: []*Change{breakingChange}}}, } assert.Equal(t, 4, i.TotalBreakingChanges()) @@ -352,188 +352,188 @@ func TestComparisonFunctions_NilReturnPatterns(t *testing.T) { // Clear hash cache to ensure deterministic results in concurrent test environments low.ClearHashCache() // Test all comparison functions that have TotalChanges() <= 0 nil return patterns - + // Create identical objects for comparison (should result in no changes) - + t.Run("Components_NoChanges_ReturnsNil", func(t *testing.T) { components := &v3.Components{} result := CompareComponents(components, components) assert.Nil(t, result, "CompareComponents should return nil when there are no changes") }) - + t.Run("Header_NoChanges_ReturnsNil", func(t *testing.T) { header := &v3.Header{} result := CompareHeaders(header, header) assert.Nil(t, result, "CompareHeaders should return nil when there are no changes") }) - + t.Run("Paths_NoChanges_ReturnsNil", func(t *testing.T) { paths := &v3.Paths{} result := ComparePaths(paths, paths) assert.Nil(t, result, "ComparePaths should return nil when there are no changes") }) - + t.Run("OAuthFlows_NoChanges_ReturnsNil", func(t *testing.T) { flows := &v3.OAuthFlows{} result := CompareOAuthFlows(flows, flows) assert.Nil(t, result, "CompareOAuthFlows should return nil when there are no changes") }) - + t.Run("OAuthFlow_NoChanges_ReturnsNil", func(t *testing.T) { flow := &v3.OAuthFlow{} result := CompareOAuthFlow(flow, flow) assert.Nil(t, result, "CompareOAuthFlow should return nil when there are no changes") }) - + t.Run("RequestBody_NoChanges_ReturnsNil", func(t *testing.T) { requestBody := &v3.RequestBody{} result := CompareRequestBodies(requestBody, requestBody) assert.Nil(t, result, "CompareRequestBodies should return nil when there are no changes") }) - + t.Run("XML_NoChanges_ReturnsNil", func(t *testing.T) { xml := &base.XML{} result := CompareXML(xml, xml) assert.Nil(t, result, "CompareXML should return nil when there are no changes") }) - + t.Run("ServerVariable_NoChanges_ReturnsNil", func(t *testing.T) { serverVar := &v3.ServerVariable{} result := CompareServerVariables(serverVar, serverVar) assert.Nil(t, result, "CompareServerVariables should return nil when there are no changes") }) - + t.Run("Responses_NoChanges_ReturnsNil", func(t *testing.T) { responses := &v3.Responses{} result := CompareResponses(responses, responses) assert.Nil(t, result, "CompareResponses should return nil when there are no changes") }) - + t.Run("Items_NoChanges_ReturnsNil", func(t *testing.T) { items := &v2.Items{} result := CompareItems(items, items) assert.Nil(t, result, "CompareItems should return nil when there are no changes") }) - + t.Run("Response_NoChanges_ReturnsNil", func(t *testing.T) { response := &v3.Response{} result := CompareResponseV3(response, response) assert.Nil(t, result, "CompareResponseV3 should return nil when there are no changes") }) - + t.Run("Info_NoChanges_ReturnsNil", func(t *testing.T) { info := &base.Info{} result := CompareInfo(info, info) assert.Nil(t, result, "CompareInfo should return nil when there are no changes") }) - + t.Run("Server_NoChanges_ReturnsNil", func(t *testing.T) { server := &v3.Server{} result := CompareServers(server, server) assert.Nil(t, result, "CompareServers should return nil when there are no changes") }) - + t.Run("Discriminator_NoChanges_ReturnsNil", func(t *testing.T) { discriminator := &base.Discriminator{} result := CompareDiscriminator(discriminator, discriminator) assert.Nil(t, result, "CompareDiscriminator should return nil when there are no changes") }) - + t.Run("Extensions_NoChanges_ReturnsNil", func(t *testing.T) { result := CompareExtensions(nil, nil) assert.Nil(t, result, "CompareExtensions should return nil when there are no changes") }) - + t.Run("SecurityScheme_NoChanges_ReturnsNil", func(t *testing.T) { securityScheme := &v3.SecurityScheme{} result := CompareSecuritySchemes(securityScheme, securityScheme) assert.Nil(t, result, "CompareSecuritySchemes should return nil when there are no changes") }) - + t.Run("Contact_NoChanges_ReturnsNil", func(t *testing.T) { contact := &base.Contact{} result := CompareContact(contact, contact) assert.Nil(t, result, "CompareContact should return nil when there are no changes") }) - + t.Run("Encoding_NoChanges_ReturnsNil", func(t *testing.T) { encoding := &v3.Encoding{} result := CompareEncoding(encoding, encoding) assert.Nil(t, result, "CompareEncoding should return nil when there are no changes") }) - + t.Run("ExternalDocs_NoChanges_ReturnsNil", func(t *testing.T) { externalDocs := &base.ExternalDoc{} result := CompareExternalDocs(externalDocs, externalDocs) assert.Nil(t, result, "CompareExternalDocs should return nil when there are no changes") }) - + t.Run("MediaType_NoChanges_ReturnsNil", func(t *testing.T) { mediaType := &v3.MediaType{} result := CompareMediaTypes(mediaType, mediaType) assert.Nil(t, result, "CompareMediaTypes should return nil when there are no changes") }) - + t.Run("Parameter_NoChanges_ReturnsNil", func(t *testing.T) { parameter := &v3.Parameter{} result := CompareParametersV3(parameter, parameter) assert.Nil(t, result, "CompareParametersV3 should return nil when there are no changes") }) - + t.Run("SecurityRequirement_NoChanges_ReturnsNil", func(t *testing.T) { securityReq := &base.SecurityRequirement{} result := CompareSecurityRequirement(securityReq, securityReq) assert.Nil(t, result, "CompareSecurityRequirement should return nil when there are no changes") }) - + t.Run("Example_NoChanges_ReturnsNil", func(t *testing.T) { example := &base.Example{} result := CompareExamples(example, example) assert.Nil(t, result, "CompareExamples should return nil when there are no changes") }) - + t.Run("Scopes_NoChanges_ReturnsNil", func(t *testing.T) { scopes := &v2.Scopes{} result := CompareScopes(scopes, scopes) assert.Nil(t, result, "CompareScopes should return nil when there are no changes") }) - + t.Run("Operation_NoChanges_ReturnsNil", func(t *testing.T) { operation := &v3.Operation{} result := CompareOperations(operation, operation) assert.Nil(t, result, "CompareOperations should return nil when there are no changes") }) - + t.Run("Examples_NoChanges_ReturnsNil", func(t *testing.T) { examples := &v2.Examples{} result := CompareExamplesV2(examples, examples) assert.Nil(t, result, "CompareExamplesV2 should return nil when there are no changes") }) - + t.Run("Callback_NoChanges_ReturnsNil", func(t *testing.T) { callback := &v3.Callback{} result := CompareCallback(callback, callback) assert.Nil(t, result, "CompareCallback should return nil when there are no changes") }) - + t.Run("Document_NoChanges_ReturnsNil", func(t *testing.T) { document := &v3.Document{} result := CompareDocuments(document, document) assert.Nil(t, result, "CompareDocuments should return nil when there are no changes") }) - + t.Run("PathItem_NoChanges_ReturnsNil", func(t *testing.T) { pathItem := &v3.PathItem{} result := ComparePathItems(pathItem, pathItem) assert.Nil(t, result, "ComparePathItems should return nil when there are no changes") }) - + t.Run("Link_NoChanges_ReturnsNil", func(t *testing.T) { link := &v3.Link{} result := CompareLinks(link, link) assert.Nil(t, result, "CompareLinks should return nil when there are no changes") }) - + t.Run("License_NoChanges_ReturnsNil", func(t *testing.T) { license := &base.License{} result := CompareLicense(license, license) @@ -547,7 +547,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { // Clear hash cache to ensure deterministic results in concurrent test environments low.ClearHashCache() // Test all 31 instances of the TotalChanges() <= 0 pattern found by grep - + // 1. components.go:203 t.Run("ComponentsChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { components1 := &v3.Components{} @@ -555,7 +555,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareComponents(components1, components2) assert.Nil(t, result, "CompareComponents should return nil when TotalChanges() <= 0") }) - + // 2. server_variable.go:84 t.Run("ServerVariableChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { serverVar1 := &v3.ServerVariable{} @@ -563,7 +563,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareServerVariables(serverVar1, serverVar2) assert.Nil(t, result, "CompareServerVariables should return nil when TotalChanges() <= 0") }) - + // 3. header.go:285 t.Run("HeaderChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { // Create v2 headers that will bypass equality check but have no changes @@ -572,7 +572,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareHeaders(header1, header2) assert.Nil(t, result, "CompareHeaders should return nil when TotalChanges() <= 0") }) - + // 4. request_body.go:98 t.Run("RequestBodyChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { requestBody1 := &v3.RequestBody{} @@ -580,7 +580,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareRequestBodies(requestBody1, requestBody2) assert.Nil(t, result, "CompareRequestBodies should return nil when TotalChanges() <= 0") }) - + // 5. paths.go:228 t.Run("PathsChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { paths1 := &v3.Paths{} @@ -588,7 +588,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := ComparePaths(paths1, paths2) assert.Nil(t, result, "ComparePaths should return nil when TotalChanges() <= 0") }) - + // 6. link.go:165 t.Run("LinkChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { link1 := &v3.Link{} @@ -596,7 +596,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareLinks(link1, link2) assert.Nil(t, result, "CompareLinks should return nil when TotalChanges() <= 0") }) - + // 7. xml.go:116 t.Run("XMLChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { xml1 := &base.XML{} @@ -604,7 +604,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareXML(xml1, xml2) assert.Nil(t, result, "CompareXML should return nil when TotalChanges() <= 0") }) - + // 8. oauth_flows.go:159 (CompareOAuthFlows) t.Run("OAuthFlowsChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { flows1 := &v3.OAuthFlows{} @@ -612,7 +612,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareOAuthFlows(flows1, flows2) assert.Nil(t, result, "CompareOAuthFlows should return nil when TotalChanges() <= 0") }) - + // 9. oauth_flows.go:267 (CompareOAuthFlow) t.Run("OAuthFlowChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { flow1 := &v3.OAuthFlow{} @@ -620,13 +620,13 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareOAuthFlow(flow1, flow2) assert.Nil(t, result, "CompareOAuthFlow should return nil when TotalChanges() <= 0") }) - + // 10. extensions.go:87 t.Run("ExtensionChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { result := CompareExtensions(nil, nil) assert.Nil(t, result, "CompareExtensions should return nil when TotalChanges() <= 0") }) - + // 11. scopes.go:81 t.Run("ScopesChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { scopes1 := &v2.Scopes{} @@ -634,7 +634,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareScopes(scopes1, scopes2) assert.Nil(t, result, "CompareScopes should return nil when TotalChanges() <= 0") }) - + // 12. response.go:200 t.Run("ResponseChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { response1 := &v3.Response{} @@ -642,7 +642,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareResponseV3(response1, response2) assert.Nil(t, result, "CompareResponseV3 should return nil when TotalChanges() <= 0") }) - + // 13. document.go:295 t.Run("DocumentChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { document1 := &v3.Document{} @@ -650,7 +650,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareDocuments(document1, document2) assert.Nil(t, result, "CompareDocuments should return nil when TotalChanges() <= 0") }) - + // 14. example.go:212 t.Run("ExampleChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { example1 := &base.Example{} @@ -658,7 +658,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareExamples(example1, example2) assert.Nil(t, result, "CompareExamples should return nil when TotalChanges() <= 0") }) - + // 15. items.go:88 t.Run("ItemsChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { items1 := &v2.Items{} @@ -666,7 +666,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareItems(items1, items2) assert.Nil(t, result, "CompareItems should return nil when TotalChanges() <= 0") }) - + // 16. callback.go:116 t.Run("CallbackChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { callback1 := &v3.Callback{} @@ -674,7 +674,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareCallback(callback1, callback2) assert.Nil(t, result, "CompareCallback should return nil when TotalChanges() <= 0") }) - + // 17. license.go:94 t.Run("LicenseChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { license1 := &base.License{} @@ -682,7 +682,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareLicense(license1, license2) assert.Nil(t, result, "CompareLicense should return nil when TotalChanges() <= 0") }) - + // 18. server.go:97 t.Run("ServerChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { server1 := &v3.Server{} @@ -690,7 +690,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareServers(server1, server2) assert.Nil(t, result, "CompareServers should return nil when TotalChanges() <= 0") }) - + // 19. encoding.go:100 t.Run("EncodingChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { encoding1 := &v3.Encoding{} @@ -698,7 +698,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareEncoding(encoding1, encoding2) assert.Nil(t, result, "CompareEncoding should return nil when TotalChanges() <= 0") }) - + // 20. external_docs.go:84 t.Run("ExternalDocsChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { extDocs1 := &base.ExternalDoc{} @@ -706,7 +706,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareExternalDocs(extDocs1, extDocs2) assert.Nil(t, result, "CompareExternalDocs should return nil when TotalChanges() <= 0") }) - + // 21. examples.go:88 t.Run("ExamplesChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { examples1 := &v2.Examples{} @@ -714,7 +714,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareExamplesV2(examples1, examples2) assert.Nil(t, result, "CompareExamplesV2 should return nil when TotalChanges() <= 0") }) - + // 22. contact.go:82 t.Run("ContactChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { contact1 := &base.Contact{} @@ -722,7 +722,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareContact(contact1, contact2) assert.Nil(t, result, "CompareContact should return nil when TotalChanges() <= 0") }) - + // 23. parameter.go:342 t.Run("ParameterChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { param1 := &v3.Parameter{} @@ -730,7 +730,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareParametersV3(param1, param2) assert.Nil(t, result, "CompareParametersV3 should return nil when TotalChanges() <= 0") }) - + // 24. media_type.go:152 t.Run("MediaTypeChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { mediaType1 := &v3.MediaType{} @@ -738,7 +738,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareMediaTypes(mediaType1, mediaType2) assert.Nil(t, result, "CompareMediaTypes should return nil when TotalChanges() <= 0") }) - + // 25. discriminator.go:97 t.Run("DiscriminatorChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { discriminator1 := &base.Discriminator{} @@ -746,7 +746,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareDiscriminator(discriminator1, discriminator2) assert.Nil(t, result, "CompareDiscriminator should return nil when TotalChanges() <= 0") }) - + // 26. security_scheme.go:186 t.Run("SecuritySchemeChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { secScheme1 := &v3.SecurityScheme{} @@ -754,7 +754,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareSecuritySchemes(secScheme1, secScheme2) assert.Nil(t, result, "CompareSecuritySchemes should return nil when TotalChanges() <= 0") }) - + // 27. path_item.go:223 t.Run("PathItemChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { pathItem1 := &v3.PathItem{} @@ -762,7 +762,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := ComparePathItems(pathItem1, pathItem2) assert.Nil(t, result, "ComparePathItems should return nil when TotalChanges() <= 0") }) - + // 28. info.go:160 t.Run("InfoChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { info1 := &base.Info{} @@ -770,7 +770,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareInfo(info1, info2) assert.Nil(t, result, "CompareInfo should return nil when TotalChanges() <= 0") }) - + // 29. security_requirement.go:51 t.Run("SecurityRequirementChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { secReq1 := &base.SecurityRequirement{} @@ -778,7 +778,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareSecurityRequirement(secReq1, secReq2) assert.Nil(t, result, "CompareSecurityRequirement should return nil when TotalChanges() <= 0") }) - + // 30. operation.go:428 t.Run("OperationChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { operation1 := &v3.Operation{} @@ -786,7 +786,7 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareOperations(operation1, operation2) assert.Nil(t, result, "CompareOperations should return nil when TotalChanges() <= 0") }) - + // 31. responses.go:145 t.Run("ResponsesChanges_TotalChangesZero_ReturnsNil", func(t *testing.T) { responses1 := &v3.Responses{} @@ -794,4 +794,4 @@ func TestTotalChangesZeroReturnNil(t *testing.T) { result := CompareResponses(responses1, responses2) assert.Nil(t, result, "CompareResponses should return nil when TotalChanges() <= 0") }) -} \ No newline at end of file +} diff --git a/what-changed/model/oauth_flows_test.go b/what-changed/model/oauth_flows_test.go index 261ff83d..20b78f1a 100644 --- a/what-changed/model/oauth_flows_test.go +++ b/what-changed/model/oauth_flows_test.go @@ -456,7 +456,7 @@ func TestCompareOAuthFlows_DeviceFlowModified(t *testing.T) { assert.NotNil(t, extChanges.DeviceChanges) // DeviceChanges should be included in GetAllChanges and TotalChanges - assert.Equal(t, 2, extChanges.TotalChanges()) // tokenUrl change + scope addition + assert.Equal(t, 2, extChanges.TotalChanges()) // tokenUrl change + scope addition assert.Equal(t, 1, extChanges.TotalBreakingChanges()) // tokenUrl change is breaking allChanges := extChanges.GetAllChanges() diff --git a/what-changed/model/operation.go b/what-changed/model/operation.go index 53b4477e..66436239 100644 --- a/what-changed/model/operation.go +++ b/what-changed/model/operation.go @@ -234,12 +234,12 @@ func CompareOperations(l, r any) *OperationChanges { for i := range lParams { s := lParams[i].Value.Name.Value lv[s] = lParams[i].Value - lRefs[s] = &lParams[i] // Keep the reference wrapper + lRefs[s] = &lParams[i] // Keep the reference wrapper } for i := range rParams { s := rParams[i].Value.Name.Value rv[s] = rParams[i].Value - rRefs[s] = &rParams[i] // Keep the reference wrapper + rRefs[s] = &rParams[i] // Keep the reference wrapper } var paramChanges []*ParameterChanges @@ -343,12 +343,12 @@ func CompareOperations(l, r any) *OperationChanges { for i := range lParams { s := lParams[i].Value.Name.Value lv[s] = lParams[i].Value - lRefs[s] = &lParams[i] // Keep the reference wrapper + lRefs[s] = &lParams[i] // Keep the reference wrapper } for i := range rParams { s := rParams[i].Value.Name.Value rv[s] = rParams[i].Value - rRefs[s] = &rParams[i] // Keep the reference wrapper + rRefs[s] = &rParams[i] // Keep the reference wrapper } var paramChanges []*ParameterChanges diff --git a/what-changed/model/path_item.go b/what-changed/model/path_item.go index ee442d47..803be6f6 100644 --- a/what-changed/model/path_item.go +++ b/what-changed/model/path_item.go @@ -440,12 +440,12 @@ func checkParameters(lParams, rParams []low.ValueReference[low.SharedParameters] for i := range lParams { s := lParams[i].Value.GetName().Value lv[s] = lParams[i].Value - lRefs[s] = &lParams[i] // Keep the reference wrapper + lRefs[s] = &lParams[i] // Keep the reference wrapper } for i := range rParams { s := rParams[i].Value.GetName().Value rv[s] = rParams[i].Value - rRefs[s] = &rParams[i] // Keep the reference wrapper + rRefs[s] = &rParams[i] // Keep the reference wrapper } var paramChanges []*ParameterChanges diff --git a/what-changed/model/responses_test.go b/what-changed/model/responses_test.go index d098d6f9..7df45dba 100644 --- a/what-changed/model/responses_test.go +++ b/what-changed/model/responses_test.go @@ -526,7 +526,7 @@ func TestCompareResponses_V3_ConfigurableCodesBreaking(t *testing.T) { // test 1: default behavior - removing codes is breaking, adding is not changes := CompareResponses(&lDoc, &rDoc) - assert.Equal(t, 3, changes.TotalChanges()) // 2 removed (404, 500) + 1 added (418) + assert.Equal(t, 3, changes.TotalChanges()) // 2 removed (404, 500) + 1 added (418) assert.Equal(t, 2, changes.TotalBreakingChanges()) // only removals are breaking by default // test 2: custom config - make removals non-breaking @@ -540,7 +540,7 @@ func TestCompareResponses_V3_ConfigurableCodesBreaking(t *testing.T) { SetActiveBreakingRulesConfig(customConfig) changes2 := CompareResponses(&lDoc, &rDoc) - assert.Equal(t, 3, changes2.TotalChanges()) // same number of changes + assert.Equal(t, 3, changes2.TotalChanges()) // same number of changes assert.Equal(t, 0, changes2.TotalBreakingChanges()) // no breaking changes now // test 3: custom config - make additions breaking diff --git a/what-changed/model/schema_test.go b/what-changed/model/schema_test.go index 53622a74..4ea65260 100644 --- a/what-changed/model/schema_test.go +++ b/what-changed/model/schema_test.go @@ -4467,11 +4467,11 @@ components: changes := CompareSchemas(lSchemaProxy, rSchemaProxy) assert.NotNil(t, changes) - + // Test GetAllChanges includes DependentRequired changes allChanges := changes.GetAllChanges() assert.Greater(t, len(allChanges), 0) - + // Verify at least one DependentRequired change is included foundDepReq := false for _, change := range allChanges { @@ -4517,7 +4517,7 @@ components: changes := CompareSchemas(lSchemaProxy, rSchemaProxy) assert.NotNil(t, changes) - + // Test TotalBreakingChanges includes DependentRequired breaking changes totalBreaking := changes.TotalBreakingChanges() assert.Greater(t, totalBreaking, 0) @@ -4530,15 +4530,15 @@ func TestSlicesEqual_AllCases(t *testing.T) { a := []string{"name", "email"} b := []string{"name", "email"} assert.True(t, slicesEqual(a, b)) - + // Test different lengths c := []string{"name"} assert.False(t, slicesEqual(a, c)) - + // Test different content d := []string{"name", "phone"} assert.False(t, slicesEqual(a, d)) - + // Test empty slices assert.True(t, slicesEqual([]string{}, []string{})) } @@ -4549,14 +4549,14 @@ func TestGetNodeForProperty_EdgeCases(t *testing.T) { // Test with nil map (line 1778-1779) node := getNodeForProperty(nil, "test") assert.Nil(t, node) - + // Test with property not found (line 1785) depMap := orderedmap.New[low.KeyReference[string], low.ValueReference[[]string]]() depMap.Set(low.KeyReference[string]{Value: "billing"}, low.ValueReference[[]string]{Value: []string{"name"}}) - + node = getNodeForProperty(depMap, "nonexistent") assert.Nil(t, node) - + // Test with property found (should return the node) node = getNodeForProperty(depMap, "billing") // Note: In this test case the node will be nil since we didn't set ValueNode, @@ -4578,7 +4578,7 @@ components: leftDoc, _ := test_BuildDoc(spec, spec) lSchemaProxy := leftDoc.Components.Value.FindSchema("Something").Value - + // Access the low-level DependentRequired to test with real nodes lowSchema := lSchemaProxy.Schema() if lowSchema.DependentRequired.Value != nil { @@ -4625,11 +4625,11 @@ components: changes := CompareSchemas(lSchemaProxy, rSchemaProxy) assert.NotNil(t, changes) assert.Greater(t, len(changes.DependentRequiredChanges), 0) - + // This specifically calls GetPropertyChanges() which contains lines 73-74 propertyChanges := changes.GetPropertyChanges() assert.Greater(t, len(propertyChanges), 0) - + // Verify that DependentRequired changes are included in property changes foundDepReq := false for _, change := range propertyChanges { diff --git a/what-changed/model/security_requirement_test.go b/what-changed/model/security_requirement_test.go index 9bd53100..e9058b09 100644 --- a/what-changed/model/security_requirement_test.go +++ b/what-changed/model/security_requirement_test.go @@ -593,7 +593,7 @@ ApiKey: []` assert.Equal(t, 1, extChanges.TotalChanges()) assert.Equal(t, 1, extChanges.TotalBreakingChanges()) assert.Equal(t, ObjectRemoved, extChanges.Changes[0].ChangeType) - assert.Equal(t, "ApiKey", extChanges.Changes[0].Property) // Verify scheme name in property + assert.Equal(t, "ApiKey", extChanges.Changes[0].Property) // Verify scheme name in property assert.Equal(t, "ApiKey", extChanges.Changes[0].Original) // Verify scheme name in original value (entire scheme removed) }