Skip to content

Commit 8e16469

Browse files
authored
chore: refactor package layout (#15)
* chore: package disentanglement This PR restructures the codescan package in order to: * get a better control over exposed API vs internal (currently: Run(), Options) * organize the functionality in smaller bits, more easily identifiable and testable * establish a clear chain of responsibility between the various components How this has been tested: * all the original tests remain the same with the same assertions and have been reshuffled across packages * all tests that produce a spec or partial spec output on master got their output captured as a "golden" JSON file: all reshuffled tests compare their output against this golden copy. * added more tests to the baseline, producing golden with an extended coverage (~ 85%) * added more tests to the baseline, exercising reachable error paths * added regression test harness with full golden comparison Signed-off-by: Frederic BIDON <fredbi@yahoo.com> * chore: backport to refactored layout commit "swagger:type array by falling through to underlying type resolution" Signed-off-by: Frederic BIDON <fredbi@yahoo.com> --------- Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
1 parent 6923606 commit 8e16469

200 files changed

Lines changed: 25268 additions & 9575 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ profile.cov
2727
# .idea/
2828
# .vscode/
2929
.mcp.json
30+
.worktrees

.mockery.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
all: false
2+
dir: '{{.InterfaceDir}}'
3+
filename: mocks_test.go
4+
force-file-write: true
5+
formatter: goimports
6+
include-auto-generated: false
7+
log-level: info
8+
structname: '{{.Mock}}{{.InterfaceName}}'
9+
pkgname: '{{.SrcPackageName}}'
10+
recursive: false
11+
require-template-schema-exists: true
12+
template: matryer
13+
template-schema: '{{.Template}}.schema.json'
14+
packages:
15+
github.com/go-openapi/codescan/internal/ifaces:
16+
config:
17+
dir: internal/scantest/mocks
18+
filename: mocks.go
19+
pkgname: 'mocks'
20+
force-file-write: true
21+
all: true

api.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package codescan
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/go-openapi/codescan/internal/builders/spec"
10+
"github.com/go-openapi/codescan/internal/scanner"
11+
oaispec "github.com/go-openapi/spec"
12+
)
13+
14+
// Options for the scanner.
15+
type Options = scanner.Options
16+
17+
// Run the scanner to produce a swagger spec with the options provided.
18+
func Run(opts *Options) (*oaispec.Swagger, error) { // TODO(fred/claude): use option functors pattern
19+
ctx, err := scanner.NewScanCtx(opts)
20+
if err != nil {
21+
return nil, fmt.Errorf("could not scan source: %w: %w", err, ErrCodeScan)
22+
}
23+
24+
builder := spec.NewBuilder(opts.InputSpec, ctx, opts.ScanModels) // TODO(fred/claude): use option functors pattern
25+
sp, err := builder.Build()
26+
if err != nil {
27+
return nil, fmt.Errorf("could not build spec: %w: %w", err, ErrCodeScan)
28+
}
29+
30+
return sp, nil
31+
}

api_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package codescan
5+
6+
import (
7+
"flag"
8+
"io"
9+
"log"
10+
"os"
11+
"path/filepath"
12+
"testing"
13+
14+
"github.com/go-openapi/testify/v2/require"
15+
)
16+
17+
// Public-API smoke suite. Fixture-heavy tests live in internal/integration.
18+
19+
var enableDebug bool //nolint:gochecknoglobals // test flag registered in init
20+
21+
func init() { //nolint:gochecknoinits // registers test flags before TestMain
22+
flag.BoolVar(&enableDebug, "enable-debug", false, "enable debug output in tests")
23+
}
24+
25+
func TestMain(m *testing.M) {
26+
flag.Parse()
27+
28+
if !enableDebug {
29+
log.SetOutput(io.Discard)
30+
} else {
31+
log.SetFlags(log.LstdFlags | log.Lshortfile)
32+
log.SetOutput(os.Stderr)
33+
}
34+
35+
os.Exit(m.Run())
36+
}
37+
38+
func TestApplication_DebugLogging(t *testing.T) {
39+
// Exercises the logger.DebugLogf code path with Debug: true.
40+
_, err := Run(&Options{
41+
Packages: []string{"./goparsing/petstore/..."},
42+
WorkDir: "fixtures",
43+
ScanModels: true,
44+
Debug: true,
45+
})
46+
47+
require.NoError(t, err)
48+
}
49+
50+
func TestRun_InvalidWorkDir(t *testing.T) {
51+
// Exercises the Run() error path when package loading fails.
52+
_, err := Run(&Options{
53+
Packages: []string{"./..."},
54+
WorkDir: "/nonexistent/directory",
55+
})
56+
57+
require.Error(t, err)
58+
}
59+
60+
func TestSetEnumDoesNotPanic(t *testing.T) {
61+
// Regression: ensure Run() does not panic on minimal source with an enum.
62+
dir := t.TempDir()
63+
64+
src := `
65+
package failure
66+
67+
// swagger:model Order
68+
type Order struct {
69+
State State ` + "`json:\"state\"`" + `
70+
}
71+
72+
// State represents the state of an order.
73+
// enum: ["created","processed"]
74+
type State string
75+
`
76+
err := os.WriteFile(filepath.Join(dir, "model.go"), []byte(src), 0o600)
77+
require.NoError(t, err)
78+
79+
goMod := `
80+
module failure
81+
go 1.23`
82+
err = os.WriteFile(filepath.Join(dir, "go.mod"), []byte(goMod), 0o600)
83+
require.NoError(t, err)
84+
85+
_, err = Run(&Options{
86+
WorkDir: dir,
87+
ScanModels: true,
88+
})
89+
90+
require.NoError(t, err)
91+
}

0 commit comments

Comments
 (0)