Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check response payload. #347

Merged
merged 1 commit into from
Jun 19, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Added `distribution` field to `OpenSearchVersionInfo` ([#336](https://github.com/opensearch-project/opensearch-api-specification/pull/336))
- Added `created_time` and `last_updated_time` to `ml.get_model_group@200` ([#342](https://github.com/opensearch-project/opensearch-api-specification/pull/342))
- Added spellcheck linter ([#341](https://github.com/opensearch-project/opensearch-api-specification/pull/341))
- Added tests for response payload ([#347](https://github.com/opensearch-project/opensearch-api-specification/pull/347))

### Changed

Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"eslint-plugin-yml": "^1.14.0",
"globals": "^15.0.0",
"jest": "^29.7.0",
"json-diff-ts": "^4.0.1",
"json-schema-to-typescript": "^14.0.4",
"ts-jest": "^29.1.2"
}
Expand Down
4 changes: 4 additions & 0 deletions tests/_core/info.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ chapters:
pretty: false
response:
status: 200
payload:
version:
distribution: opensearch
tagline: 'The OpenSearch Project: https://opensearch.org/'
33 changes: 28 additions & 5 deletions tools/src/tester/ChapterEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* compatible open source license.
*/

import { type Chapter, type ActualResponse } from './types/story.types'
import { type Chapter, type ActualResponse, type Payload } from './types/story.types'
import { type ChapterEvaluation, type Evaluation, Result } from './types/eval.types'
import { type ParsedOperation } from './types/spec.types'
import { overall_result } from './helpers'
Expand All @@ -16,6 +16,8 @@ import type OperationLocator from './OperationLocator'
import type SchemaValidator from './SchemaValidator'
import { type StoryOutputs } from './StoryOutputs'
import { ChapterOutput } from './ChapterOutput'
import { Operation, atomizeChangeset, diff } from 'json-diff-ts'
import _ from 'lodash'

export default class ChapterEvaluator {
private readonly _operation_locator: OperationLocator
Expand All @@ -36,13 +38,20 @@ export default class ChapterEvaluator {
const params = this.#evaluate_parameters(chapter, operation)
const request_body = this.#evaluate_request_body(chapter, operation)
const status = this.#evaluate_status(chapter, response)
const payload = status.result === Result.PASSED ? this.#evaluate_payload(response, operation) : { result: Result.SKIPPED }
const payload_body_evaluation = status.result === Result.PASSED ? this.#evaluate_payload_body(response, chapter.response?.payload) : { result: Result.SKIPPED }
const payload_schema_evaluation = status.result === Result.PASSED ? this.#evaluate_payload_schema(response, operation) : { result: Result.SKIPPED }
const output_values = ChapterOutput.extract_output_values(response, chapter.output)
return {
title: chapter.synopsis,
overall: { result: overall_result(Object.values(params).concat([request_body, status, payload]).concat(output_values ? [output_values] : [])) },
overall: { result: overall_result(Object.values(params).concat([
request_body, status, payload_body_evaluation, payload_schema_evaluation
]).concat(output_values ? [output_values] : [])) },
request: { parameters: params, request_body },
response: { status, payload },
response: {
status,
payload_body: payload_body_evaluation,
payload_schema: payload_schema_evaluation
},
...(output_values ? { output_values } : {})
}
}
Expand Down Expand Up @@ -74,7 +83,21 @@ export default class ChapterEvaluator {
}
}

#evaluate_payload(response: ActualResponse, operation: ParsedOperation): Evaluation {
#evaluate_payload_body(response: ActualResponse, expected_payload?: Payload): Evaluation {
if (expected_payload == null) return { result: Result.PASSED }
const delta = atomizeChangeset(diff(expected_payload, response.payload))
const messages: string[] = _.compact(delta.map((value, _index, _array) => {
switch(value.type) {
case Operation.UPDATE:
return `expected ${value.path.replace('$.', '')}='${value.oldValue}', got '${value.value}'`
case Operation.REMOVE:
return `missing ${value.path.replace('$.', '')}='${value.value}'`
}
}))
return messages.length > 0 ? { result: Result.FAILED, message: _.join(messages, ', ')} : { result: Result.PASSED }
}

#evaluate_payload_schema(response: ActualResponse, operation: ParsedOperation): Evaluation {
const content_type = response.content_type ?? 'application/json'
const content = operation.responses[response.status]?.content[content_type]
const schema = content?.schema
Expand Down
12 changes: 9 additions & 3 deletions tools/src/tester/ResultLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ export class ConsoleResultLogger implements ResultLogger {
this.#log_parameters(chapter.request?.parameters ?? {})
this.#log_request_body(chapter.request?.request_body)
this.#log_status(chapter.response?.status)
this.#log_payload(chapter.response?.payload)
this.#log_payload_body(chapter.response?.payload_body)
this.#log_payload_schema(chapter.response?.payload_schema)
}

#log_parameters (parameters: Record<string, Evaluation>): void {
Expand All @@ -75,9 +76,14 @@ export class ConsoleResultLogger implements ResultLogger {
this.#log_evaluation(evaluation, 'RESPONSE STATUS', this._tab_width * 3)
}

#log_payload (evaluation: Evaluation | undefined): void {
#log_payload_body (evaluation: Evaluation | undefined): void {
if (evaluation == null) return
this.#log_evaluation(evaluation, 'RESPONSE PAYLOAD', this._tab_width * 3)
this.#log_evaluation(evaluation, 'RESPONSE PAYLOAD BODY', this._tab_width * 3)
}

#log_payload_schema (evaluation: Evaluation | undefined): void {
if (evaluation == null) return
this.#log_evaluation(evaluation, 'RESPONSE PAYLOAD SCHEMA', this._tab_width * 3)
}

#log_evaluation (evaluation: Evaluation, title: string, prefix: number = 0): void {
Expand Down
3 changes: 2 additions & 1 deletion tools/src/tester/types/eval.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export interface ChapterEvaluation {
}
response?: {
status: Evaluation
payload: Evaluation
payload_body: Evaluation,
payload_schema: Evaluation
}
output_values?: EvaluationWithOutput
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ chapters:
message: 'Expected status 200, but received 404: application/json. no such index
[undefined]'
error: Request failed with status code 404
payload:
payload_body:
result: SKIPPED
payload_schema:
result: SKIPPED
- title: This chapter should be skipped.
overall:
Expand Down
15 changes: 11 additions & 4 deletions tools/tests/tester/fixtures/evals/failed/invalid_data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: PASSED
payload_schema:
result: PASSED
- title: This chapter should fail because the request body is invalid.
overall:
Expand All @@ -35,9 +37,11 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: PASSED
payload_schema:
result: PASSED
- title: This chapter should fail because the response is invalid.
- title: This chapter should fail because the response data and schema are invalid.
overall:
result: FAILED
request:
Expand All @@ -49,7 +53,10 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: FAILED
message: expected acknowledged='false', got 'true', missing shards_acknowledged='true'
payload_schema:
result: FAILED
message: data must NOT have additional properties

Expand Down
12 changes: 9 additions & 3 deletions tools/tests/tester/fixtures/evals/failed/not_found.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: PASSED
payload_schema:
result: PASSED
- title: This chapter should fail because the request body is not defined in the spec.
overall:
Expand All @@ -41,7 +43,9 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: PASSED
payload_schema:
result: PASSED
- title: This chapter should fail because the response is not defined in the spec.
overall:
Expand All @@ -55,7 +59,9 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: PASSED
payload_schema:
result: FAILED
message: 'Schema for "404: application/json" response not found in the spec.'

Expand Down
4 changes: 3 additions & 1 deletion tools/tests/tester/fixtures/evals/passed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ chapters:
response:
status:
result: PASSED
payload:
payload_body:
result: PASSED
payload_schema:
result: PASSED

epilogues:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ chapters:
request_body:
payload:
aliases: {}
- synopsis: This chapter should fail because the response is invalid.
- synopsis: This chapter should fail because the response data and schema are invalid.
path: /{index}
method: DELETE
parameters:
index: books
response:
status: 200
payload:
shards_acknowledged: true
acknowledged: false
shards_acknowledged: true
3 changes: 2 additions & 1 deletion tools/tests/tester/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ export function flatten_errors (evaluation: StoryEvaluation): StoryEvaluation {
response: c.response !== undefined
? {
status: flatten(c.response.status),
payload: flatten(c.response.payload)
payload_body: flatten(c.response.payload_body),
payload_schema: flatten(c.response.payload_schema)
}
: undefined
})) as T
Expand Down
Loading