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

Remove shared resources. #316

Closed
Closed
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
28 changes: 12 additions & 16 deletions tools/src/tester/ChapterEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,25 @@ import { type Chapter, type ActualResponse } 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'
import type ChapterReader from './ChapterReader'
import SharedResources from './SharedResources'
import type SpecParser from './SpecParser'
import type SchemaValidator from './SchemaValidator'
import { type OpenAPIV3 } from 'openapi-types'
import ChapterReader from './ChapterReader'
import SpecParser from './SpecParser'
import SchemaValidator from './SchemaValidator'

export default class ChapterEvaluator {
spec: OpenAPIV3.Document
chapter: Chapter
skip_payload_evaluation: boolean = false
spec_parser: SpecParser
chapter_reader: ChapterReader
schema_validator: SchemaValidator

constructor (chapter: Chapter) {
constructor (chapter: Chapter, spec: OpenAPIV3.Document) {
this.chapter = chapter
this.spec_parser = SharedResources.get_instance().spec_parser
this.chapter_reader = SharedResources.get_instance().chapter_reader
this.schema_validator = SharedResources.get_instance().schema_validator
this.spec = spec
}

async evaluate (skip: boolean): Promise<ChapterEvaluation> {
if (skip) return { title: this.chapter.synopsis, overall: { result: Result.SKIPPED } }
const response = await this.chapter_reader.read(this.chapter)
const operation = this.spec_parser.locate_operation(this.chapter)
const response = await new ChapterReader().read(this.chapter)
const operation = new SpecParser(this.spec).locate_operation(this.chapter)
if (operation == null) return { title: this.chapter.synopsis, overall: { result: Result.FAILED, message: `Operation "${this.chapter.method.toUpperCase()} ${this.chapter.path}" not found in the spec.` } }
const params = this.#evaluate_parameters(operation)
const request_body = this.#evaluate_request_body(operation)
Expand All @@ -51,7 +47,7 @@ export default class ChapterEvaluator {
return Object.fromEntries(Object.entries(this.chapter.parameters ?? {}).map(([name, parameter]) => {
const schema = operation.parameters[name]?.schema
if (schema == null) return [name, { result: Result.FAILED, message: `Schema for "${name}" parameter not found.` }]
const evaluation = this.schema_validator.validate(schema, parameter)
const evaluation = new SchemaValidator(this.spec).validate(schema, parameter)
return [name, evaluation]
}))
}
Expand All @@ -61,7 +57,7 @@ export default class ChapterEvaluator {
const content_type = this.chapter.request_body.content_type ?? 'application/json'
const schema = operation.requestBody?.content[content_type]?.schema
if (schema == null) return { result: Result.FAILED, message: `Schema for "${content_type}" request body not found in the spec.` }
return this.schema_validator.validate(schema, this.chapter.request_body?.payload ?? {})
return new SchemaValidator(this.spec).validate(schema, this.chapter.request_body?.payload ?? {})
}

#evaluate_status (response: ActualResponse): Evaluation {
Expand All @@ -82,6 +78,6 @@ export default class ChapterEvaluator {
const schema = content?.schema
if (schema == null && content != null) return { result: Result.PASSED }
if (schema == null) return { result: Result.FAILED, message: `Schema for "${response.status}: ${response.content_type}" response not found in the spec.` }
return this.schema_validator.validate(schema, response.payload)
return new SchemaValidator(this.spec).validate(schema, response.payload)
}
}
41 changes: 0 additions & 41 deletions tools/src/tester/SharedResources.ts

This file was deleted.

14 changes: 7 additions & 7 deletions tools/src/tester/StoryEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
* compatible open source license.
*/

import { type OpenAPIV3 } from 'openapi-types'
import { type Chapter, type Story, type SupplementalChapter } from './types/story.types'
import { type ChapterEvaluation, Result, type StoryEvaluation } from './types/eval.types'
import ChapterEvaluator from './ChapterEvaluator'
import type ChapterReader from './ChapterReader'
import SharedResources from './SharedResources'
import { overall_result } from './helpers'
import ChapterReader from './ChapterReader'

export interface StoryFile {
display_path: string
Expand All @@ -21,19 +21,19 @@ export interface StoryFile {
}

export default class StoryEvaluator {
spec: OpenAPIV3.Document
dry_run: boolean
story: Story
display_path: string
full_path: string
has_errors: boolean = false
chapter_reader: ChapterReader

constructor (story_file: StoryFile, dry_run?: boolean) {
constructor (story_file: StoryFile, spec: OpenAPIV3.Document, dry_run?: boolean) {
this.spec = spec
this.dry_run = dry_run ?? false
this.story = story_file.story
this.display_path = story_file.display_path
this.full_path = story_file.full_path
this.chapter_reader = SharedResources.get_instance().chapter_reader
}

async evaluate (): Promise<StoryEvaluation> {
Expand Down Expand Up @@ -67,7 +67,7 @@ export default class StoryEvaluator {
const title = chapter.synopsis || `${chapter.method} ${chapter.path}`
evaluations.push({ title, overall: { result: Result.SKIPPED, message: 'Dry Run', error: undefined } })
} else {
const evaluator = new ChapterEvaluator(chapter)
const evaluator = new ChapterEvaluator(chapter, this.spec)
const evaluation = await evaluator.evaluate(this.has_errors)
this.has_errors = this.has_errors || evaluation.overall.result === Result.ERROR
evaluations.push(evaluation)
Expand All @@ -83,7 +83,7 @@ export default class StoryEvaluator {
if (this.dry_run) {
evaluations.push({ title, overall: { result: Result.SKIPPED, message: 'Dry Run', error: undefined } })
} else {
const response = await this.chapter_reader.read(chapter)
const response = await new ChapterReader().read(chapter)
const status = chapter.status ?? [200, 201]
if (status.includes(response.status)) evaluations.push({ title, overall: { result: Result.PASSED } })
else {
Expand Down
13 changes: 3 additions & 10 deletions tools/src/tester/TestsRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,33 @@
*/

import { type OpenAPIV3 } from 'openapi-types'
import SpecParser from './SpecParser'
import ChapterReader from './ChapterReader'
import SchemaValidator from './SchemaValidator'
import StoryEvaluator, { type StoryFile } from './StoryEvaluator'
import fs from 'fs'
import { type Story } from './types/story.types'
import { read_yaml } from '../../helpers'
import { Result, type StoryEvaluation } from './types/eval.types'
import ResultsDisplayer, { type TestRunOptions, type DisplayOptions } from './ResultsDisplayer'
import SharedResources from './SharedResources'
import { resolve, basename } from 'path'

type TestsRunnerOptions = TestRunOptions & DisplayOptions & Record<string, any>

export default class TestsRunner {
path: string // Path to a story file or a directory containing story files
opts: TestsRunnerOptions
spec: OpenAPIV3.Document

constructor (spec: OpenAPIV3.Document, path: string, opts: TestsRunnerOptions) {
this.path = resolve(path)
this.opts = opts

const chapter_reader = new ChapterReader()
const spec_parser = new SpecParser(spec)
const schema_validator = new SchemaValidator(spec)
SharedResources.create_instance({ chapter_reader, schema_validator, spec_parser })
this.spec = spec
}

async run (debug: boolean = false): Promise<StoryEvaluation[]> {
let failed = false
const story_files = this.#collect_story_files(this.path, '', '')
const evaluations: StoryEvaluation[] = []
for (const story_file of this.#sort_story_files(story_files)) {
const evaluator = new StoryEvaluator(story_file, this.opts.dry_run)
const evaluator = new StoryEvaluator(story_file, this.spec, this.opts.dry_run)
const evaluation = await evaluator.evaluate()
const displayer = new ResultsDisplayer(evaluation, this.opts)
if (debug) evaluations.push(evaluation)
Expand Down
1 change: 1 addition & 0 deletions tools/src/tester/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const options = {

// The fallback password must match the default password specified in .github/opensearch-cluster/docker-compose.yml
process.env.OPENSEARCH_PASSWORD = process.env.OPENSEARCH_PASSWORD ?? 'myStrongPassword123!'

const spec = (new OpenApiMerger(opts.specPath, LogLevel.error)).merge()
const runner = new TestsRunner(spec, opts.testsPath, options)
void runner.run().then(() => { _.noop() })
83 changes: 45 additions & 38 deletions tools/tests/tester/StoryEvaluator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,52 @@
* compatible open source license.
*/

import { create_shared_resources, load_actual_evaluation, load_expected_evaluation } from './helpers'
import { load_actual_evaluation, load_expected_evaluation } from './helpers'
import { read_yaml } from '../../helpers'
import { type OpenAPIV3 } from 'openapi-types'

const spec = read_yaml('tools/tests/tester/fixtures/specs/indices_excerpt.yaml')
create_shared_resources(spec as OpenAPIV3.Document)

test('passed', async () => {
const actual = await load_actual_evaluation('passed')
const expected = await load_expected_evaluation('passed')
expect(actual).toEqual(expected)
})

test('skipped', async () => {
const actual = await load_actual_evaluation('skipped')
const expected = await load_expected_evaluation('skipped')
expect(actual).toEqual(expected)
})

test('failed/not_found', async () => {
const actual = await load_actual_evaluation('failed/not_found')
const expected = await load_expected_evaluation('failed/not_found')
expect(actual).toEqual(expected)
})

test('failed/invalid_data', async () => {
const actual = await load_actual_evaluation('failed/invalid_data')
const expected = await load_expected_evaluation('failed/invalid_data')
expect(actual).toEqual(expected)
})

test('error/prologue_error', async () => {
const actual = await load_actual_evaluation('error/prologue_error')
const expected = await load_expected_evaluation('error/prologue_error')
expect(actual).toEqual(expected)
})

test('error/chapter_error', async () => {
const actual = await load_actual_evaluation('error/chapter_error')
const expected = await load_expected_evaluation('error/chapter_error')
expect(actual).toEqual(expected)
describe('StoryEvaluator', () => {
let spec: OpenAPIV3.Document

beforeAll(() => {
// The fallback password must match the default password specified in .github/opensearch-cluster/docker-compose.yml
process.env.OPENSEARCH_PASSWORD = process.env.OPENSEARCH_PASSWORD ?? 'myStrongPassword123!'
spec = read_yaml('tools/tests/tester/fixtures/specs/indices_excerpt.yaml')
})

test('passed', async () => {
const actual = await load_actual_evaluation('passed', spec)
const expected = await load_expected_evaluation('passed')
expect(actual).toEqual(expected)
})

test('skipped', async () => {
const actual = await load_actual_evaluation('skipped', spec)
const expected = await load_expected_evaluation('skipped')
expect(actual).toEqual(expected)
})

test('failed/not_found', async () => {
const actual = await load_actual_evaluation('failed/not_found', spec)
const expected = await load_expected_evaluation('failed/not_found')
expect(actual).toEqual(expected)
})

test('failed/invalid_data', async () => {
const actual = await load_actual_evaluation('failed/invalid_data', spec)
const expected = await load_expected_evaluation('failed/invalid_data')
expect(actual).toEqual(expected)
})

test('error/prologue_error', async () => {
const actual = await load_actual_evaluation('error/prologue_error', spec)
const expected = await load_expected_evaluation('error/prologue_error')
expect(actual).toEqual(expected)
})

test('error/chapter_error', async () => {
const actual = await load_actual_evaluation('error/chapter_error', spec)
const expected = await load_expected_evaluation('error/chapter_error')
expect(actual).toEqual(expected)
})
})
46 changes: 27 additions & 19 deletions tools/tests/tester/TestsRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,33 @@ import TestsRunner from '../../src/tester/TestsRunner'
import { type OpenAPIV3 } from 'openapi-types'
import { load_expected_evaluation, scrub_errors } from './helpers'

test('stories folder', async () => {
// The fallback password must match the default password specified in .github/opensearch-cluster/docker-compose.yml
process.env.OPENSEARCH_PASSWORD = process.env.OPENSEARCH_PASSWORD ?? 'myStrongPassword123!'
const spec = read_yaml('tools/tests/tester/fixtures/specs/indices_excerpt.yaml')
const runner = new TestsRunner(spec as OpenAPIV3.Document, 'tools/tests/tester/fixtures/stories', {})
const actual_evaluations = await runner.run(true) as any[]
for (const evaluation of actual_evaluations) scrub_errors(evaluation)
for (const evaluation of actual_evaluations) {
expect(evaluation.full_path.endsWith(evaluation.display_path)).toBeTruthy()
delete evaluation.full_path
}
describe('stories folder', () => {
let spec: OpenAPIV3.Document

const skipped = await load_expected_evaluation('skipped', true)
const passed = await load_expected_evaluation('passed', true)
const not_found = await load_expected_evaluation('failed/not_found', true)
const invalid_data = await load_expected_evaluation('failed/invalid_data', true)
const chapter_error = await load_expected_evaluation('error/chapter_error', true)
const prologue_error = await load_expected_evaluation('error/prologue_error', true)
beforeAll(() => {
// The fallback password must match the default password specified in .github/opensearch-cluster/docker-compose.yml
process.env.OPENSEARCH_PASSWORD = process.env.OPENSEARCH_PASSWORD ?? 'myStrongPassword123!'
spec = read_yaml('tools/tests/tester/fixtures/specs/indices_excerpt.yaml')
})

const expected_evaluations = [passed, skipped, chapter_error, prologue_error, invalid_data, not_found]
expect(actual_evaluations).toEqual(expected_evaluations)
test('results', async () => {
const runner = new TestsRunner(spec, 'tools/tests/tester/fixtures/stories', {})
const actual_evaluations = await runner.run(true) as any[]

for (const evaluation of actual_evaluations) {
scrub_errors(evaluation)
expect(evaluation.full_path.endsWith(evaluation.display_path)).toBeTruthy()
delete evaluation.full_path
}

const skipped = await load_expected_evaluation('skipped', true)
const passed = await load_expected_evaluation('passed', true)
const not_found = await load_expected_evaluation('failed/not_found', true)
const invalid_data = await load_expected_evaluation('failed/invalid_data', true)
const chapter_error = await load_expected_evaluation('error/chapter_error', true)
const prologue_error = await load_expected_evaluation('error/prologue_error', true)

const expected_evaluations = [passed, skipped, chapter_error, prologue_error, invalid_data, not_found]
expect(actual_evaluations).toEqual(expected_evaluations)
})
})
Loading
Loading