@@ -2,7 +2,9 @@ const amf = require("amf-client-js")
22const util = require ( "./utilities" )
33
44class RequestSpecGenerator {
5- constructor ( resolved ) {
5+ constructor ( resolved , tags , target ) {
6+ this . tags = tags
7+ this . target = target || "state"
68 this . resolved = resolved
79 this . schemas = { }
810 this . spec = undefined
@@ -18,7 +20,7 @@ class RequestSpecGenerator {
1820 requestBindings ( operation , response , requestBodyInfo ) {
1921 if ( ! this . supportsRequestBody ( operation ) ) return [ ]
2022
21- var paramsBinding , headersBinding = 'headers.merge! "content-type" => "application/json"'
23+ var paramsBinding , headersBinding = 'headers[ "content-type"] = "application/json"'
2224
2325 var requestBody = response . customDomainProperties . find ( d => d . name . value ( ) == "specs.requestBody" ) ?. extension ?. value ?. value ( ) ,
2426 attributesStrategy = this . factoryAttributesStrategy ( response ) ,
@@ -62,7 +64,7 @@ class RequestSpecGenerator {
6264
6365 permissionLevel = permissionLevel . extension . value . value ( )
6466
65- return `let(:api_key) { create :api_key, :${ permissionLevel } , user: api_key_user }`
67+ return `let(:api_key) { op_create :api_key, :${ permissionLevel } , params: { user: api_key_user.id.to_s } ${ this . bindingContext ( ) } }`
6668 }
6769
6870 idBindings ( response ) {
@@ -78,7 +80,7 @@ class RequestSpecGenerator {
7880 if ( idValue == "auto" || idValue . startsWith ( "auto:" ) ) {
7981 var traits = idValue . split ( ":" ) . slice ( 1 ) ,
8082 bindTraits = traits . length == 0 ? "" : `, ${ traits . map ( t => ":" + t ) . join ( ", " ) } `
81- bindValue = `${ factoryStrategy } (:${ factoryName || model } ${ bindTraits } ).id.to_s`
83+ bindValue = `${ factoryStrategy } (:${ factoryName || model } ${ bindTraits } ${ this . bindingContext ( ) } ).id.to_s`
8284 } else {
8385 bindValue = `"${ idValue } "`
8486 }
@@ -114,6 +116,7 @@ class RequestSpecGenerator {
114116 return `
115117 it "${ this . testCaseDescription ( requestBodyInfo ) } "${ this . testCaseFlags ( requestBodyInfo ) } do
116118 ${ bindings . join ( "\n" ) }
119+ ${ this . authorizationHeaders ( response ) }
117120 ${ operation . method . value ( ) } "${ this . basePath } ${ this . endPointPath ( endPoint , idBindings ) } "${ this . requestOptions ( operation , bindings [ 0 ] ) }
118121
119122 expect(response).to have_http_status(${ code } )
@@ -152,6 +155,43 @@ class RequestSpecGenerator {
152155 } ) . join ( "\n\n" ) ;
153156 }
154157
158+ baseContextBinding ( response ) {
159+ var baseCtx = util . findCustomDomainProperty ( response , "specs.baseContext" )
160+
161+ if ( ! baseCtx ) return ""
162+
163+ return `let(:context) { ${ baseCtx . extension . value . value ( ) } }`
164+ }
165+
166+ contextualResponseTestCases ( endPoint , operation , response , code , idBindings , bodyExpectation ) {
167+ var ctxs = util . findCustomDomainProperty ( response , "specs.contextualRequestBodies" ) ?. extension ?. members
168+ if ( ! ctxs ) return ""
169+
170+ return ctxs . map ( ctxMember => {
171+ const ctx = ctxMember . value . value ( )
172+
173+ return `
174+ context "when the context is a ${ ctx . replace ( "@" , "" ) } " do
175+ let(:context) { ${ ctx } }
176+
177+ ${ this . responseTestCase ( endPoint , operation , response , code , idBindings , bodyExpectation ) }
178+ end
179+ `
180+ } ) . join ( "\n\n" )
181+ }
182+
183+ authorizationHeaders ( response ) {
184+ const authStrategy = util . findCustomDomainProperty ( response , "specs.authStrategy" ) ?. extension ?. value ?. value ( )
185+
186+ if ( authStrategy === "session" ) {
187+ return `
188+ headers["authorization"] = "Session"
189+ headers["cookie"] = "session_token=#{api_key_user.remember_token}"
190+ headers["sec-fetch-site"] = "same-origin"
191+ `
192+ } else return ""
193+ }
194+
155195 beforeHook ( response ) {
156196 var hook = util . findCustomDomainProperty ( response , "specs.beforeHook" )
157197
@@ -170,13 +210,15 @@ class RequestSpecGenerator {
170210 var factoryName = this . factoryName ( response ) ;
171211
172212 return Object . entries ( createModels . extension . properties ) . map ( ( [ model , options ] ) => {
173- var count = options . properties . count ?. value ?. value ( ) || 3 ,
213+ var count = options . properties . count ?. value ?. value ( ) ?? 3 ,
174214 traits = options . properties . traits ?. value ?. value ( ) ?. split ( ":" ) || [ ] ,
175215 traitsSection = traits . length == 0 ? "" : `, ${ traits . map ( t => ":" + t ) . join ( ", " ) } ` ,
176216 factoryOptions = Object . entries ( options . properties . options ?. properties || { } ) ,
177217 factoryOptionsSection = factoryOptions . length == 0 ? "" : `, ${ factoryOptions . map ( ( [ opt , val ] ) => `${ opt } : ${ val . value . value ( ) } ` ) . join ( ", " ) } `
178218
179- return `before { ${ strategy } _list :${ factoryName || model } , ${ count } ${ traitsSection } ${ factoryOptionsSection } }`
219+ if ( count <= 0 ) return ""
220+
221+ return `before { ${ strategy } _list :${ factoryName || model } , ${ count } ${ traitsSection } ${ factoryOptionsSection } ${ this . bindingContext ( ) } }`
180222 } ) . join ( "\n" )
181223 }
182224
@@ -202,24 +244,28 @@ class RequestSpecGenerator {
202244 context "${ code } (${ response . description . value ( ) . trim ( ) . replace ( / \. $ / , "" ) } )" do
203245 ${ this . apiKeyBinding ( response ) }
204246 ${ idBindings }
247+ ${ this . baseContextBinding ( response ) }
205248
206249 ${ this . beforeHook ( response ) }
207250 ${ this . createModelsHook ( response ) }
208251
209252 ${ this . responseTestCase ( endPoint , operation , response , code , idBindings , bodyExpectation ) }
210253 ${ this . exhaustiveResponseTestCases ( endPoint , operation , response , code , idBindings , bodyExpectation ) }
254+ ${ this . contextualResponseTestCases ( endPoint , operation , response , code , idBindings , bodyExpectation ) }
211255 end
212256 `
213257 } , "" ) . join ( "\n\n" )
214258 }
215259
216260 operations ( ) {
217261 return this . resolved . encodes . endPoints . map ( ( endPoint ) => {
218- const resourceTag = util . findCustomDomainProperty ( endPoint , "core.tags" ) ?. extension ?. members ?. [ 0 ] ?. value ?. value ( ) || ""
262+ const resourceTags = util . findCustomDomainProperty ( endPoint , "core.tags" ) ?. extension ?. members || [ ]
263+
264+ if ( ! this . isTagEnabled ( resourceTags ) ) return ""
219265
220266 return endPoint . operations . map ( ( operation ) => {
221267 return `
222- describe "${ operation . method . value ( ) . toUpperCase ( ) } ${ this . basePath } ${ endPoint . path . value ( ) } "${ resourceTag && `, resource: "${ resourceTag } "` } do
268+ describe "${ operation . method . value ( ) . toUpperCase ( ) } ${ this . basePath } ${ endPoint . path . value ( ) } "${ ( resourceTags [ 0 ] ?? "" ) && `, resource: "${ resourceTags [ 0 ] . value . value ( ) } "` } do
223269 ${ this . responses ( endPoint , operation ) }
224270 end
225271 `
@@ -232,16 +278,44 @@ class RequestSpecGenerator {
232278 require "rails_helper"
233279
234280 RSpec.describe "${ this . basePath } (version: latest)", :timeline_simulation do
235- before(:example) { DatabaseCleaner.clean }
236- let(:api_key_user) { create :user, :admin }
237- let(:api_key) { create :api_key, :write, user: api_key_user }
281+ before(${ this . target === "website" ? ":context" : ":example" } ) { DatabaseCleaner.clean }
282+ ${ this . rootBeforeHook ( ) }
283+
284+ let(:api_key_user) { op_create :user, :admin${ this . bindingContext ( ) } }
285+ let(:api_key) { op_create :api_key, :write, params: { user: api_key_user.id.to_s }${ this . bindingContext ( ) } }
286+
238287 let(:headers) { { accept: "application/hal+json;variant=compact", authorization: "Bearer #{api_key.token}" } }
239288
289+ ${ this . rootContextBindings ( ) }
290+
240291 ${ content }
241292 end
242293 `
243294 }
244295
296+ rootBeforeHook ( ) {
297+ if ( this . target !== "website" ) return ""
298+
299+ return `
300+ before(:context) do
301+ @client = VCR.use_cassette("op_create/client") { op_create :client }
302+ @primary_status_page = @client.status_pages.first
303+ @secondary_status_page = op_create :status_page, client: @client
304+ end
305+ `
306+ }
307+
308+ rootContextBindings ( ) {
309+ if ( this . target !== "website" ) return ""
310+
311+ return `
312+ let(:context) { @primary_status_page }
313+ let(:context_host) { "#{context.domain}.hund.localhost" }
314+
315+ before { host! context_host }
316+ `
317+ }
318+
245319 schemaId ( schema ) {
246320 return schema . id
247321 . split ( "#" ) [ 1 ]
@@ -250,6 +324,18 @@ class RequestSpecGenerator {
250324 . replace ( / % / g, "_" )
251325 }
252326
327+ isTagEnabled ( tags ) {
328+ if ( ! this . tags ) return true
329+
330+ return tags . some ( tag => this . tags . includes ( tag . value . value ( ) ) )
331+ }
332+
333+ bindingContext ( ) {
334+ if ( this . target === "website" ) return ", client: @client"
335+
336+ return ""
337+ }
338+
253339 supportsRequestBody ( operation ) {
254340 return [ "post" , "put" , "patch" , "delete" ] . includes ( operation . method . value ( ) )
255341 }
0 commit comments