Skip to content

Commit

Permalink
Validate test story descriptions. (opensearch-project#467)
Browse files Browse the repository at this point in the history
* Refactored validate_description and title into a base class.

Signed-off-by: dblock <[email protected]>

* Validate story descriptions.

Signed-off-by: dblock <[email protected]>

* Use a pattern to validate story synopsis and description.

Signed-off-by: dblock <[email protected]>

---------

Signed-off-by: dblock <[email protected]>
  • Loading branch information
dblock authored Aug 5, 2024
1 parent 21bd106 commit c7dbaaf
Show file tree
Hide file tree
Showing 21 changed files with 59 additions and 41 deletions.
2 changes: 2 additions & 0 deletions json_schemas/test_story.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ properties:
type: string
description:
type: string
pattern: ^\p{Lu}[\s\S]*\.$
prologues:
type: array
items:
Expand All @@ -32,6 +33,7 @@ definitions:
synopsis:
type: string
description: A brief description of the chapter.
pattern: ^\p{Lu}[\s\S]*\.$
response:
$ref: '#/definitions/ExpectedResponse'
warnings:
Expand Down
2 changes: 1 addition & 1 deletion tests/_core/reindex/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ prologues:
method: PUT
request:
payload:
description: |
description: |-
Splits the `title`` field into a `words` list.
Computes the length of the title field and stores it in a new `length` field.
Removes the `year` field.
Expand Down
2 changes: 1 addition & 1 deletion tests/indices/aliases/put_alias.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ chapters:
payload:
acknowledged: true

- synopsis: Retrieve aliases
- synopsis: Retrieve aliases.
path: /{index}/_alias
method: GET
parameters:
Expand Down
3 changes: 1 addition & 2 deletions tests/ingest/pipeline.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
$schema: ../../json_schemas/test_story.schema.yaml

description: |
Test the creation of an ingest pipeline with a text embedding processor.
description: Test the creation of an ingest pipeline with a text embedding processor.
epilogues:
- path: /_ingest/pipeline/books_pipeline
method: DELETE
Expand Down
5 changes: 2 additions & 3 deletions tests/ml/model_groups.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
$schema: ../../json_schemas/test_story.schema.yaml

description: |
Test the creation of model groups.
description: Test the creation of model groups.
version: '>= 2.11'
prologues:
- path: /_cluster/settings
Expand All @@ -28,7 +27,7 @@ chapters:
request:
payload:
name: NLP_Group
description: Model group for NLP models
description: Model group for NLP models.
response:
status: 200
output:
Expand Down
3 changes: 1 addition & 2 deletions tests/ml/models.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
$schema: ../../json_schemas/test_story.schema.yaml

description: |
Test the creation of models.
description: Test the creation of models.
version: '>= 2.11'
prologues:
- path: /_cluster/settings
Expand Down
2 changes: 1 addition & 1 deletion tests/ppl/explain.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ epilogues:
method: DELETE
status: [200, 404]
chapters:
- synopsis: Get explain of SQL Query
- synopsis: Get explain of SQL Query.
path: /_plugins/_ppl/_explain
method: POST
request:
Expand Down
2 changes: 1 addition & 1 deletion tests/ppl/query.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ epilogues:
method: DELETE
status: [200, 404]
chapters:
- synopsis: Get PPL query
- synopsis: Get PPL query.
path: /_plugins/_ppl
method: POST
request:
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/close.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
$schema: ../../json_schemas/test_story.schema.yaml

description: Test to explicitly clear the cursor context
description: Test to explicitly clear the cursor context.

prologues:
- path: /{index}
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/explain.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ epilogues:
method: DELETE
status: [200, 404]
chapters:
- synopsis: Get explain of SQL Query
- synopsis: Get explain of SQL Query.
path: /_plugins/_sql/_explain
method: POST
request:
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/query.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ epilogues:
method: DELETE
status: [200, 404]
chapters:
- synopsis: Get SQL query
- synopsis: Get SQL query.
path: /_plugins/_sql
method: POST
parameters:
Expand Down
13 changes: 2 additions & 11 deletions tools/src/linter/components/Info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
import { type OpenAPIV3 } from 'openapi-types'
import { type ValidationError } from 'types'
import ValidatorBase from './base/ValidatorBase'
import { toLaxTitleCase } from 'titlecase'

const DESCRIPTION_REGEX = /^\p{Lu}[\s\S]*\.$/u

export default class Info extends ValidatorBase {
path: string
Expand All @@ -32,16 +29,10 @@ export default class Info extends ValidatorBase {
}

validate_description (): ValidationError | undefined {
const description = this.spec?.description ?? ''
if (description === '') { return this.error('Missing description property.') }
if (!DESCRIPTION_REGEX.test(description)) { return this.error('Description must start with a capital letter and end with a period.') }
return this.validate_description_field(this.spec?.description, true)
}

validate_title (): ValidationError | undefined {
const title = this.spec?.title ?? ''
if (title === '') { return this.error('Missing title property.') }
const expected_title = toLaxTitleCase(title)
if (title.endsWith('.')) { return this.error('Title must not end with a period.') }
if (title !== expected_title) return this.error(`Title must be capitalized, expected '${expected_title}'.`)
return this.validate_title_field(this.spec?.title)
}
}
5 changes: 1 addition & 4 deletions tools/src/linter/components/Operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import _ from 'lodash'
import ValidatorBase from './base/ValidatorBase'

const GROUP_REGEX = /^([a-z]+[a-z_]*[a-z]+\.)?([a-z]+[a-z_]*[a-z]+)$/
const DESCRIPTION_REGEX = /^\p{Lu}[\s\S]*\.$/u

export default class Operation extends ValidatorBase {
path: string
Expand Down Expand Up @@ -65,9 +64,7 @@ export default class Operation extends ValidatorBase {
}

validate_description (): ValidationError | undefined {
const description = this.spec.description ?? ''
if (description === '') { return this.error('Missing description property.') }
if (!DESCRIPTION_REGEX.test(description)) return this.error('Description must start with a capital letter and end with a period.')
return this.validate_description_field(this.spec?.description, true)
}

validate_operation_id (): ValidationError | undefined {
Expand Down
5 changes: 1 addition & 4 deletions tools/src/linter/components/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { type OpenAPIV3 } from 'openapi-types'
import { type ValidationError } from 'types'

const NAME_REGEX = /^[A-Za-z0-9]+$/
const DESCRIPTION_REGEX = /^\p{Lu}[\s\S]*\.$/u

export default class Schema extends ValidatorBase {
name: string
Expand All @@ -36,8 +35,6 @@ export default class Schema extends ValidatorBase {
}

validate_description (): ValidationError | undefined {
const description = this.spec.description ?? ''
if (description === '') return
if (!DESCRIPTION_REGEX.test(description)) { return this.error('Description must start with a capital letter and end with a period.') }
return this.validate_description_field(this.spec.description)
}
}
18 changes: 18 additions & 0 deletions tools/src/linter/components/base/ValidatorBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
* compatible open source license.
*/

import { toLaxTitleCase } from 'titlecase'
import { type ValidationError } from 'types'

const DESCRIPTION_REGEX = /^\p{Lu}[\s\S]*\.$/u

export default class ValidatorBase {
file: string
location: string | undefined
Expand All @@ -24,4 +28,18 @@ export default class ValidatorBase {
validate (): ValidationError[] {
throw new Error('Method not implemented.')
}

validate_description_field (value?: string, required: boolean = false, key: string = 'description'): ValidationError | undefined {
if (value === undefined) { return required ? this.error(`Missing ${key} property.`) : undefined }
if (value === '') { return this.error(`Empty ${key} property.`) }
if (!DESCRIPTION_REGEX.test(value)) { return this.error(`The ${key} must start with a capital letter and end with a period, got "${value}".`) }
}

validate_title_field (value?: string, required: boolean = false, key: string = 'title'): ValidationError | undefined {
if (value === undefined) { return required ? this.error(`Missing ${key} property.`) : undefined }
if (value === '') { return this.error(`Empty ${key} property.`) }
const expected = toLaxTitleCase(value)
if (value.endsWith('.')) { return this.error(`The ${key} must not end with a period.`) }
if (value !== expected) return this.error(`The ${key} must be capitalized, expected "${expected}", not "${value}".`)
}
}
8 changes: 4 additions & 4 deletions tools/tests/linter/NamespaceFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ test('validate_info() periods', () => {
{
file: 'namespaces/invalid_info_periods.yaml',
location: 'Info',
message: 'Title must not end with a period.'
message: 'The title must not end with a period.'
},
{
file: 'namespaces/invalid_info_periods.yaml',
location: 'Info',
message: 'Description must start with a capital letter and end with a period.'
message: 'The description must start with a capital letter and end with a period, got "Description should have a period".'
}
])
})
Expand All @@ -126,12 +126,12 @@ test('validate_info() capitals', () => {
{
file: 'namespaces/invalid_info_capitals.yaml',
location: 'Info',
message: "Title must be capitalized, expected 'Title Must Be Capitalized'."
message: "The title must be capitalized, expected \"Title Must Be Capitalized\", not \"Title must be capitalized\"."
},
{
file: 'namespaces/invalid_info_capitals.yaml',
location: 'Info',
message: "Description must start with a capital letter and end with a period."
message: "The description must start with a capital letter and end with a period, got \"description must start with a capital letter and end with a period.\"."
}
])
})
Expand Down
2 changes: 1 addition & 1 deletion tools/tests/linter/Operation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ test('validate_description()', () => {

const invalid_description = operation({ 'x-operation-group': 'indices.create', description: 'This is a description without a period' })
expect(invalid_description.validate_description())
.toEqual(invalid_description.error('Description must start with a capital letter and end with a period.'))
.toEqual(invalid_description.error('The description must start with a capital letter and end with a period, got "This is a description without a period".'))

const valid_description = operation({ 'x-operation-group': 'indices.create', description: 'This is a description with a period.' })
expect(valid_description.validate_description())
Expand Down
2 changes: 1 addition & 1 deletion tools/tests/linter/Schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ test('validate_description()', () => {
expect(schema('Name', { description: 'Does not end with a period' }).validate_description()).toEqual({
file: '_common.yaml',
location: '#/components/schemas/Name',
message: "Description must start with a capital letter and end with a period."
message: "The description must start with a capital letter and end with a period, got \"Does not end with a period\"."
})
})
8 changes: 8 additions & 0 deletions tools/tests/tester/StoryValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ describe('StoryValidator', () => {
"data/chapters/1/method MUST be equal to one of the allowed values: GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS")
})

test('invalid description', () => {
const evaluation = validate('tools/tests/tester/fixtures/invalid_description.yaml')
expect(evaluation?.result).toBe('ERROR')
expect(evaluation?.message).toBe("Invalid Story: " +
"data/description must match pattern \"^\\p{Lu}[\\s\\S]*\\.$\" --- " +
"data/chapters/0/synopsis must match pattern \"^\\p{Lu}[\\s\\S]*\\.$\"")
})

test('valid story', () => {
const evaluation = validate('tools/tests/tester/fixtures/valid_story.yaml')
expect(evaluation).toBeUndefined()
Expand Down
8 changes: 8 additions & 0 deletions tools/tests/tester/fixtures/invalid_description.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$schema: ../../../../json_schemas/test_story.schema.yaml

description: This story description is missing a period

chapters:
- synopsis: this synopsis is not capitalized.
method: GET
path: /_cat/health
4 changes: 2 additions & 2 deletions tools/tests/tester/fixtures/invalid_story.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ $schema: ../../../../json_schemas/test_story.schema.yaml

description: This story is invalid when validated against test_story.schema.yaml.
chapters:
- synopsis: Missing Method
- synopsis: Missing method.
path: /{index}
- synopsis: Invalid Method
- synopsis: Invalid method.
path: /
method: RETRIEVE
epilogues:
Expand Down

0 comments on commit c7dbaaf

Please sign in to comment.