Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 250 additions & 54 deletions source/OpenAPI-Client-Tests/OARequestBuilderTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -40,62 +40,19 @@ OARequestBuilderTest >> testAddHeaderParameterSetsRequestHeader [
]

{ #category : 'tests' }
OARequestBuilderTest >> testWriteBodyWithAllOfComposedSchemaDoesNotCrash [
"allOf-composed schemas (no direct type/properties keyword, like petstoreExpanded's
Pet = allOf[NewPet, {id}]) resolve to JSONSchemaAnyObject via #asJSONSchema since
nothing sets schemaClass for a bare allOf. Writing a body against such a schema
must degrade to a plain passthrough rather than crash."
| definition schema mediaType client builder body |
definition := JSONSchemaDefinition new
allOf: { JSONSchemaDefinition new
properties: { 'name' -> (JSONSchemaDefinition new typeString: 'string'; yourself) } asDictionary;
yourself };
yourself.
schema := definition asJSONSchema.
self assert: schema class equals: JSONSchemaAnyObject.
mediaType := OAMediaTypeObject new schema: schema.
client := ZnClient new.
builder := OARequestBuilder new client: client.
body := Dictionary new at: 'name' put: 'Rex'; yourself.
mediaType writeBody: body builder: builder.
self assert: client request entity contents equals: '{"name":"Rex"}'
]

{ #category : 'tests' }
OARequestBuilderTest >> testWriteBodyWithNonObjectSchemaDoesNotCrash [
"Regression test: OAMediaTypeObject>>writeBody:builder: used to send #isAnyObject
unconditionally to the body schema. #isAnyObject is only implemented on
JSONSchemaObject, so any non-object body schema (bare string, array, or an
allOf-composed schema which resolves to JSONSchemaAnyObject) crashed with
#doesNotUnderstand: #isAnyObject when building a real request."
| mediaType client builder |
mediaType := OAMediaTypeObject new schema: JSONSchema string.
client := ZnClient new.
builder := OARequestBuilder new client: client.
mediaType writeBody: 'hello world' builder: builder.
self assert: client request entity contents equals: '"hello world"'
]

{ #category : 'tests' }
OARequestBuilderTest >> testWriteFormBodyWithAllOfComposedSchemaDoesNotCrash [
"Same passthrough bug as #writeBody:builder: (both send #isAnyObject
unconditionally), but for the x-www-form-urlencoded path. An allOf-composed
body schema resolves to JSONSchemaAnyObject and must still form-encode."
| definition schema mediaType client builder body |
definition := JSONSchemaDefinition new
allOf: { JSONSchemaDefinition new
properties: { 'name' -> (JSONSchemaDefinition new typeString: 'string'; yourself) } asDictionary;
yourself };
yourself.
schema := definition asJSONSchema.
self assert: schema class equals: JSONSchemaAnyObject.
mediaType := OAMediaTypeObject new schema: schema.
OARequestBuilderTest >> testCookieParameterArrayValueCommaJoins [
"Regression: cookie write:value:to: used to send #asString to the raw value - an
Array printed as garbage instead of a comma-joined string. Unlike query parameters,
a single Cookie header cannot repeat the same name for form+explode:true arrays, so
this is deliberately comma-joined rather than left as separate repeated pairs."
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
body := Dictionary new at: 'name' put: 'Rex'; yourself.
mediaType writeFormBody: body builder: builder.
self assert: client request entity contentType sub equals: 'x-www-form-urlencoded'.
self assert: (client request entity contents at: 'name') equals: 'Rex'
param := OAParameter new name: 'ids'; in: #cookie; required: true;
schema: (JSONSchemaArray new items: JSONSchema integer); yourself.
dict := Dictionary new at: 'ids' put: #(1 2); yourself.
param copyFrom: dict to: builder.
self assert: (client request headers at: 'Cookie') equals: 'ids=1,2'
]

{ #category : 'tests' }
Expand Down Expand Up @@ -169,6 +126,20 @@ OARequestBuilderTest >> testFlattenScalarValues [
self assert: (result at: 'trial_end') equals: 'now'
]

{ #category : 'tests' }
OARequestBuilderTest >> testHeaderParameterArrayValueCommaJoins [
"Regression: header write:value:to: used to send #asString to the raw value -
an Array printed as garbage (e.g. #(3 4 5)) instead of a comma-joined string."
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'X-Ids'; in: #header; required: true;
schema: (JSONSchemaArray new items: JSONSchema integer); yourself.
dict := Dictionary new at: 'X-Ids' put: #(3 4 5); yourself.
param copyFrom: dict to: builder.
self assert: (client request headers at: 'X-Ids') equals: '3,4,5'
]

{ #category : 'tests' }
OARequestBuilderTest >> testHeaderParameterWrittenViaParameterCopyFromTo [
"End-to-end: a header OAParameter must be writable via the same
Expand All @@ -181,3 +152,228 @@ OARequestBuilderTest >> testHeaderParameterWrittenViaParameterCopyFromTo [
param copyFrom: dict to: builder.
self assert: (client request headers at: 'X-Api-Key') equals: 'secret123'
]

{ #category : 'tests' }
OARequestBuilderTest >> testPathParameterIntegerValueDoesNotCrash [
"Regression: addPathSegments: requires Strings: a bare Integer path parameter value
used to crash the whole request build with #doesNotUnderstand: #readStream."
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #path; required: true; schema: JSONSchema integer; yourself.
dict := Dictionary new at: 'id' put: 5; yourself.
param copyFrom: dict to: builder.
builder path: '/pets/{id}'; baseUri: (ZnUrl new scheme: #http; host: 'x').
self shouldnt: [ builder buildClient ] raise: Error.
self assert: client request url pathPrintString equals: '/pets/5'
]

{ #category : 'tests' }
OARequestBuilderTest >> testPathParameterLabelStyleArrayExplodeTrue [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #path; required: true; style: 'label'; explode: true;
schema: (JSONSchemaArray new items: JSONSchema string); yourself.
dict := Dictionary new at: 'id' put: #('blue' 'black' 'brown'); yourself.
param copyFrom: dict to: builder.
builder path: '/colors/{id}'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url pathPrintString equals: '/colors/.blue.black.brown'
]

{ #category : 'tests' }
OARequestBuilderTest >> testPathParameterMatrixStyleArrayExplodeFalse [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #path; required: true; style: 'matrix'; explode: false;
schema: (JSONSchemaArray new items: JSONSchema integer); yourself.
dict := Dictionary new at: 'id' put: #(3 4 5); yourself.
param copyFrom: dict to: builder.
builder path: '/pets/{id}'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url pathPrintString equals: '/pets/;id=3,4,5'
]

{ #category : 'tests' }
OARequestBuilderTest >> testPathParameterMatrixStyleArrayExplodeTrue [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #path; required: true; style: 'matrix'; explode: true;
schema: (JSONSchemaArray new items: JSONSchema integer); yourself.
dict := Dictionary new at: 'id' put: #(3 4 5); yourself.
param copyFrom: dict to: builder.
builder path: '/pets/{id}'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url pathPrintString equals: '/pets/;id=3;id=4;id=5'
]

{ #category : 'tests' }
OARequestBuilderTest >> testPathParameterSimpleStyleArrayIsUnaffectedByExplode [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #path; required: true;
schema: (JSONSchemaArray new items: JSONSchema string); yourself.
dict := Dictionary new at: 'id' put: #('blue' 'black' 'brown'); yourself.
param copyFrom: dict to: builder.
builder path: '/colors/{id}'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url pathPrintString equals: '/colors/blue,black,brown'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterDeepObjectStyle [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #query; required: false; style: 'deepObject'; yourself.
dict := Dictionary new at: 'id' put: (Dictionary new at: #role put: 'admin'; yourself); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?id%5Brole%5D=admin'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterFormStyleArrayExplodeFalseCommaJoins [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'tags'; in: #query; required: false; explode: false;
schema: (JSONSchemaArray new items: JSONSchema string); yourself.
dict := Dictionary new at: 'tags' put: #('a' 'b' 'c'); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?tags=a,b,c'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterFormStyleArrayExplodeTrueDefaultRepeatsKey [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'tags'; in: #query; required: false;
schema: (JSONSchemaArray new items: JSONSchema string); yourself.
dict := Dictionary new at: 'tags' put: #('a' 'b' 'c'); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?tags=a&tags=b&tags=c'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterFormStyleObjectExplodeFalseCommaFlattens [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #query; required: false; explode: false; yourself.
dict := Dictionary new at: 'id' put: (Dictionary new at: #role put: 'admin'; yourself); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?id=role,admin'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterFormStyleObjectExplodeTrueDefaultFlattensProperties [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'id'; in: #query; required: false; yourself.
dict := Dictionary new at: 'id' put: (Dictionary new at: #role put: 'admin'; yourself); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?role=admin'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterPipeDelimitedStyle [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'tags'; in: #query; required: false; style: 'pipeDelimited';
schema: (JSONSchemaArray new items: JSONSchema string); yourself.
dict := Dictionary new at: 'tags' put: #('a' 'b' 'c'); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?tags=a%7Cb%7Cc'
]

{ #category : 'tests' }
OARequestBuilderTest >> testQueryParameterSpaceDelimitedStyle [
| client builder param dict |
client := ZnClient new.
builder := OARequestBuilder new client: client.
param := OAParameter new name: 'tags'; in: #query; required: false; style: 'spaceDelimited';
schema: (JSONSchemaArray new items: JSONSchema string); yourself.
dict := Dictionary new at: 'tags' put: #('a' 'b' 'c'); yourself.
param copyFrom: dict to: builder.
builder path: '/pets'; baseUri: (ZnUrl new scheme: #http; host: 'x').
builder buildClient.
self assert: client request url printString equals: 'http://x:80/pets?tags=a%20b%20c'
]

{ #category : 'tests' }
OARequestBuilderTest >> testWriteBodyWithAllOfComposedSchemaDoesNotCrash [
"allOf-composed schemas (no direct type/properties keyword, like petstoreExpanded's
Pet = allOf[NewPet, {id}]) resolve to JSONSchemaAnyObject via #asJSONSchema since
nothing sets schemaClass for a bare allOf. Writing a body against such a schema
must degrade to a plain passthrough rather than crash."
| definition schema mediaType client builder body |
definition := JSONSchemaDefinition new
allOf: { JSONSchemaDefinition new
properties: { 'name' -> (JSONSchemaDefinition new typeString: 'string'; yourself) } asDictionary;
yourself };
yourself.
schema := definition asJSONSchema.
self assert: schema class equals: JSONSchemaAnyObject.
mediaType := OAMediaTypeObject new schema: schema.
client := ZnClient new.
builder := OARequestBuilder new client: client.
body := Dictionary new at: 'name' put: 'Rex'; yourself.
mediaType writeBody: body builder: builder.
self assert: client request entity contents equals: '{"name":"Rex"}'
]

{ #category : 'tests' }
OARequestBuilderTest >> testWriteBodyWithNonObjectSchemaDoesNotCrash [
"Regression test: OAMediaTypeObject>>writeBody:builder: used to send #isAnyObject
unconditionally to the body schema. #isAnyObject is only implemented on
JSONSchemaObject, so any non-object body schema (bare string, array, or an
allOf-composed schema which resolves to JSONSchemaAnyObject) crashed with
#doesNotUnderstand: #isAnyObject when building a real request."
| mediaType client builder |
mediaType := OAMediaTypeObject new schema: JSONSchema string.
client := ZnClient new.
builder := OARequestBuilder new client: client.
mediaType writeBody: 'hello world' builder: builder.
self assert: client request entity contents equals: '"hello world"'
]

{ #category : 'tests' }
OARequestBuilderTest >> testWriteFormBodyWithAllOfComposedSchemaDoesNotCrash [
"Same passthrough bug as #writeBody:builder: (both send #isAnyObject
unconditionally), but for the x-www-form-urlencoded path. An allOf-composed
body schema resolves to JSONSchemaAnyObject and must still form-encode."
| definition schema mediaType client builder body |
definition := JSONSchemaDefinition new
allOf: { JSONSchemaDefinition new
properties: { 'name' -> (JSONSchemaDefinition new typeString: 'string'; yourself) } asDictionary;
yourself };
yourself.
schema := definition asJSONSchema.
self assert: schema class equals: JSONSchemaAnyObject.
mediaType := OAMediaTypeObject new schema: schema.
client := ZnClient new.
builder := OARequestBuilder new client: client.
body := Dictionary new at: 'name' put: 'Rex'; yourself.
mediaType writeFormBody: body builder: builder.
self assert: client request entity contentType sub equals: 'x-www-form-urlencoded'.
self assert: (client request entity contents at: 'name') equals: 'Rex'
]
11 changes: 9 additions & 2 deletions source/OpenAPI-Core/OACookieParameterLocation.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ Class {
{ #category : 'writing' }
OACookieParameterLocation >> write: key value: value to: builder [
"Previously missing entirely - see OAHeaderParametersLocation>>write:value:to:
for the same gap affecting 'in: #cookie' parameters."
builder addCookieParameter: key value: value
for the same gap affecting 'in: #cookie' parameters. Unlike query parameters, a
single Cookie header cannot repeat the same name for form+explode:true arrays -
comma-join instead of passing the raw collection through (the spec leaves this
combination undefined for cookies; this is the pragmatic, documented choice)."
(self serializePairsFor: value name: key defaultStyle: 'form') do: [ :pair | | v |
v := pair value.
builder addCookieParameter: pair key value: (v isString
ifTrue: [ v ]
ifFalse: [ ',' join: v ]) ]
]
4 changes: 3 additions & 1 deletion source/OpenAPI-Core/OAHeaderParametersLocation.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ OAHeaderParametersLocation >> write: key value: value to: builder [
copyFrom:to: dispatches here for any 'in: #header' parameter, and crashed with
#doesNotUnderstand: #write:value:to: for every client request using a header
parameter (e.g. an API key sent via a custom header)."
builder addHeaderParameter: key value: value
| pairs |
pairs := self serializePairsFor: value name: key defaultStyle: 'simple'.
builder addHeaderParameter: key value: pairs first value
]
Loading
Loading