1package analysis_test
2
3import (
4	"flag"
5	"io/ioutil"
6	"net/http"
7	"net/http/httptest"
8	"os"
9	"testing"
10
11	"github.com/go-openapi/analysis"
12	"github.com/go-openapi/loads"
13	"github.com/go-openapi/spec"
14	"github.com/go-openapi/swag"
15	"github.com/stretchr/testify/assert"
16	"github.com/stretchr/testify/require"
17)
18
19var enableLongTests bool
20
21func init() {
22	flag.BoolVar(&enableLongTests, "enable-long", false, "enable long runnning tests")
23}
24
25func skipNotify(t *testing.T) {
26	t.Log("To enable this long running test, use -args -enable-long in your go test command line")
27}
28
29func Test_FlattenAzure(t *testing.T) {
30	if !enableLongTests {
31		skipNotify(t)
32		t.SkipNow()
33	}
34	t.Parallel()
35
36	// Local copy of https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/network/resource-manager/Microsoft.Network/stable/2020-04-01/publicIpAddress.json
37	url := "fixtures/azure/publicIpAddress.json"
38	byts, err := swag.LoadFromFileOrHTTP(url)
39	assert.NoError(t, err)
40	swagger := &spec.Swagger{}
41	require.NoError(t, swagger.UnmarshalJSON(byts))
42
43	analyzed := analysis.New(swagger)
44	require.NoError(t, analysis.Flatten(analysis.FlattenOpts{Spec: analyzed, Expand: true, BasePath: url}))
45
46	jazon := asJSON(t, swagger)
47
48	assertRefInJSONRegexp(t, jazon, `^(#/definitions/)|(\./example)`)
49
50	t.Run("resolve local $ref azure", func(t *testing.T) {
51		assertRefResolve(t, jazon, `\./example`, swagger, &spec.ExpandOptions{RelativeBase: url})
52	})
53}
54
55func TestRemoteFlattenAzure_Expand(t *testing.T) {
56	if !enableLongTests {
57		skipNotify(t)
58		t.SkipNow()
59	}
60	t.Parallel()
61
62	server := httptest.NewServer(http.FileServer(http.Dir("fixtures/azure")))
63	defer server.Close()
64
65	basePath := server.URL + "/publicIpAddress.json"
66
67	swagger, err := loads.Spec(basePath)
68	require.NoError(t, err)
69
70	require.NoError(t, analysis.Flatten(analysis.FlattenOpts{Spec: swagger.Analyzer, Expand: true, BasePath: basePath}))
71
72	jazon := asJSON(t, swagger.Spec())
73
74	assertRefInJSONRegexp(t, jazon, `^(#/definitions/)|(\./example)`)
75
76	t.Run("resolve remote $ref azure [after expansion]", func(t *testing.T) {
77		assertRefResolve(t, jazon, `\./example`, swagger.Spec(), &spec.ExpandOptions{RelativeBase: basePath})
78	})
79}
80
81func TestRemoteFlattenAzure_Flatten(t *testing.T) {
82	if !enableLongTests {
83		skipNotify(t)
84		t.SkipNow()
85	}
86	t.Parallel()
87
88	server := httptest.NewServer(http.FileServer(http.Dir("fixtures/azure")))
89	defer server.Close()
90
91	basePath := server.URL + "/publicIpAddress.json"
92
93	swagger, err := loads.Spec(basePath)
94	require.NoError(t, err)
95
96	require.NoError(t, analysis.Flatten(analysis.FlattenOpts{Spec: swagger.Analyzer, Expand: false, BasePath: basePath}))
97
98	jazon := asJSON(t, swagger.Spec())
99
100	assertRefInJSONRegexp(t, jazon, `^(#/definitions/)|(\./example)`)
101
102	t.Run("resolve remote $ref azure [minimal flatten]", func(t *testing.T) {
103		assertRefResolve(t, jazon, `\./example`, swagger.Spec(), &spec.ExpandOptions{RelativeBase: basePath})
104	})
105}
106
107func TestIssue66(t *testing.T) {
108	// no BasePath provided: assume current working directory
109	file, clean := makeFileSpec(t)
110	defer clean()
111
112	// analyze and expand
113	doc, err := loads.Spec(file)
114	require.NoError(t, err)
115	an := analysis.New(doc.Spec()) // Analyze spec
116	require.NoError(t, analysis.Flatten(analysis.FlattenOpts{
117		Spec:   an,
118		Expand: true,
119	}))
120	jazon := asJSON(t, doc.Spec())
121	assertNoRef(t, jazon)
122
123	// reload and flatten
124	doc, err = loads.Spec(file)
125	require.NoError(t, err)
126	require.NoError(t, analysis.Flatten(analysis.FlattenOpts{
127		Spec:   an,
128		Expand: false,
129	}))
130	jazon = asJSON(t, doc.Spec())
131	t.Run("resolve $ref issue66", func(t *testing.T) {
132		assertRefResolve(t, jazon, "", doc.Spec(), &spec.ExpandOptions{})
133	})
134}
135
136func makeFileSpec(t testing.TB) (string, func()) {
137	file := "./openapi.yaml"
138	require.NoError(t, ioutil.WriteFile(file, fixtureIssue66(), 0600))
139	return file, func() {
140		_ = os.Remove(file)
141	}
142}
143
144func fixtureIssue66() []byte {
145	return []byte(`
146	x-google-endpoints:
147  - name: bravo-api.endpoints.dev-srplatform.cloud.goog
148    allowCors: true
149host: bravo-api.endpoints.dev-srplatform.cloud.goog
150swagger: '2.0'
151info:
152  description: Demo API for Bravo team testing
153  title: BRAVO Team API
154  version: 0.0.0
155basePath: /bravo
156x-google-allow: all
157consumes:
158  - application/json
159produces:
160  - application/json
161schemes:
162  - http
163  - https
164paths:
165  /bravo-api:
166    get:
167      description: List expansions
168      operationId: default
169      responses:
170        200:
171          description: Default Path
172          schema:
173            $ref: '#/definitions/heartbeatResponse'
174        403:
175          description: Forbidden
176        500:
177          description: Internal Server Error
178  /bravo-api/healthN:
179    get:
180      description: N Health
181      operationId: healthN
182      responses:
183        200:
184          description: Default Path
185          schema:
186            $ref: '#/definitions/heartbeatResponse'
187        403:
188          description: Forbidden
189        500:
190          description: Internal Server Error
191  /bravo-api/internal/heartbeat:
192    get:
193      description: Heartbeat endpoint
194      operationId: heartbeat
195      produces:
196        - application/json
197      responses:
198        200:
199          description: Health Status
200          schema:
201            $ref: '#/definitions/heartbeatResponse'
202  /bravo-api/internal/version:
203    get:
204      description: Version endpoint
205      operationId: version
206      produces:
207        - application/json
208      responses:
209        200:
210          description: Version Information
211  /bravo-api/internal/cpuload:
212    get:
213      description: CPU Load endpoint
214      operationId: cpuload
215      produces:
216        - application/json
217      responses:
218        200:
219          description: Run a CPU load
220
221definitions:
222  heartbeatResponse:
223    properties:
224      Status:
225        type: string
226      ProjectID:
227        type: string
228      Version:
229        type: string
230securityDefinitions:
231  okta_jwt:
232    authorizationUrl: "http://okta.example.com"
233    flow: "implicit"
234    type: "oauth2"
235    scopes:
236      com.sr.messaging: 'View and manage messaging content, criteria and definitions.'
237    x-google-issuer: "http://okta.example.com"
238    x-google-jwks_uri: "http://okta.example.com/v1/keys"
239    x-google-audiences: "http://api.example.com"
240	`)
241}
242