diff --git a/CHANGELOG.md b/CHANGELOG.md index 45be9002a..c196f2807 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,16 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added missing `status` to `/_search/template` response ([#702](https://github.com/opensearch-project/opensearch-api-specification/pull/702)) - Added `_type` to `rank_eval` API specs ([#704](https://github.com/opensearch-project/opensearch-api-specification/pull/704)) - Added request body to `_search_shards` API specs ([#709](https://github.com/opensearch-project/opensearch-api-specification/pull/709)) -- Added string type in additionalProperties to `msearch template` ([#x](https://github.com/opensearch-project/opensearch-api-specification/pull/x)) +- Added NodeAttributes as additional property in `shard_stores` API specs ([#715](https://github.com/opensearch-project/opensearch-api-specification/pull/715)) +- Added missing `repository` query parameter to `/_cat/snapshots` ([#700](https://github.com/opensearch-project/opensearch-api-specification/pull/700)) +- Added `sm` namespace API specifications ([#701](https://github.com/opensearch-project/opensearch-api-specification/pull/701)) +- Added schema for `/_plugins/_knn/stats`, `/_plugins/_knn/models/{model_id}`, `_train` and `_search` ([#704](https://github.com/opensearch-project/opensearch-api-specification/pull/704)) +- Added `retry` support in `prologues` and `epilogues` ([#713](https://github.com/opensearch-project/opensearch-api-specification/pull/713)) +- Added response schema for `DELETE /_plugins/_rollup/jobs/{id}`, `POST /_plugins/_rollup/jobs/{id}/_start` and `_stop` ([#716](https://github.com/opensearch-project/opensearch-api-specification/pull/716)) +- Added response schema for `PUT` and `DELETE /_plugins/_transform/{id}` ([#722](https://github.com/opensearch-project/opensearch-api-specification/pull/716)) +- Added response schema for `GET /_plugins/_knn/warmup/{index}` ([#717](https://github.com/opensearch-project/opensearch-api-specification/pull/717)) +- Added support for multiple test verbs ([#724](https://github.com/opensearch-project/opensearch-api-specification/pull/724)) +- Added string type in additionalProperties to `msearch template` ([#735](https://github.com/opensearch-project/opensearch-api-specification/pull/735)) ### Removed - Removed unsupported `_common.mapping:SourceField`'s `mode` field and associated `_common.mapping:SourceFieldMode` enum ([#652](https://github.com/opensearch-project/opensearch-api-specification/pull/652)) @@ -35,6 +44,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Removed invalid `externalDocs` from `flow_framework.create/update::query.use_case` ([#646](https://github.com/opensearch-project/opensearch-api-specification/pull/646)) - Removed unsupported `cause` and `create` from `/_index_template/_simulate_index/{name}` ([#691](https://github.com/opensearch-project/opensearch-api-specification/pull/691)) - Removed `mappings` from `required` in `indices.simulate_template#Template` ([#691](https://github.com/opensearch-project/opensearch-api-specification/pull/691)) +- Removed `HealthStatusCapatilized` and merged it with `HealthStatus` ([#725](https://github.com/opensearch-project/opensearch-api-specification/pull/725)) ### Fixed - Spec passes OpenAPI 3.1.0 validations ([#646](https://github.com/opensearch-project/opensearch-api-specification/pull/646)) @@ -52,12 +62,20 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Fixed `/_ingest/pipeline/{id}/_simulate` response `docs/_source` field schema ([#689](https://github.com/opensearch-project/opensearch-api-specification/pull/689)) - Fixed `/_scripts/painless/_execute` request and response schema ([#699](https://github.com/opensearch-project/opensearch-api-specification/pull/699)) - Fixed `fields` in `Hit` allowing primitive arrays ([#699](https://github.com/opensearch-project/opensearch-api-specification/pull/699)) -- Added missing `repository` query parameter to `/_cat/snapshots` ([#700](https://github.com/opensearch-project/opensearch-api-specification/pull/700)) - Fixed `hits` in `rank_eval` allowing numbers ([#704](https://github.com/opensearch-project/opensearch-api-specification/pull/704)) - Fixed query DSL schemas ([#706](https://github.com/opensearch-project/opensearch-api-specification/pull/706)) +- Fixed content-type of `GET /_plugins/_observability/_local/stats` ([#711](https://github.com/opensearch-project/opensearch-api-specification/pull/711)) +- Fixed `tenant` in `ObservabilityObject` request body to not be required ([#711](https://github.com/opensearch-project/opensearch-api-specification/pull/711)) +- Fixed response code in `PUT /_plugins/_rollup/jobs/{id}` ([#716](https://github.com/opensearch-project/opensearch-api-specification/pull/716)) +- Fixed response schema for `/_render/template` and `/_render/template/{id}` ([#724](https://github.com/opensearch-project/opensearch-api-specification/pull/724)) +- Fixed data stream schema numeric property types ([#725](https://github.com/opensearch-project/opensearch-api-specification/pull/725)) +- Fixed snapshot status numeric property types ([#729](https://github.com/opensearch-project/opensearch-api-specification/pull/729)) ### Changed - Changed `tasks._common:TaskInfo` and `tasks._common:TaskGroup` to be composed of a `tasks._common:TaskInfoBase` ([#683](https://github.com/opensearch-project/opensearch-api-specification/pull/683)) +- Changed `SearchModelsQuery`, `CreateConnectorRequest` & `RegisterAgentsRequest` to be defined inline of request bodies ([#725](https://github.com/opensearch-project/opensearch-api-specification/pull/725)) +- Changed `indices.data_streams_stats:DataStreamsStatsItem` to instead be `indices._common:DataStreamStats` ([#725](https://github.com/opensearch-project/opensearch-api-specification/pull/725)) +- Changed naming of `snapshot._common`'s `Status`, `ShardsStats`, `ShardsStatsStage`, `ShardsStatsSummary` and `ShardsStatsSummaryItem` schemas to be prefixed with `Snapshot` ([#730](https://github.com/opensearch-project/opensearch-api-specification/pull/730)) ## [0.1.0] - 2024-10-25 @@ -165,7 +183,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added API spec for `adjust_pure_negative` for bool queries ([#641](https://github.com/opensearch-project/opensearch-api-specification/pull/641)) - Added a spec style checker [#620](https://github.com/opensearch-project/opensearch-api-specification/pull/620). - Added `remote_store` to node `Stats` ([#643](https://github.com/opensearch-project/opensearch-api-specification/pull/643)) -- Added `sm` namespace API specifications ([#701](https://github.com/opensearch-project/opensearch-api-specification/pull/701)) ### Changed diff --git a/TESTING_GUIDE.md b/TESTING_GUIDE.md index fbb0802d4..362f6359f 100644 --- a/TESTING_GUIDE.md +++ b/TESTING_GUIDE.md @@ -12,6 +12,7 @@ - [FAILED Cat with a json response (from security-analytics).](#failed--cat-with-a-json-response-from-security-analytics) - [Writing Spec Tests](#writing-spec-tests) - [Simple Test Story](#simple-test-story) + - [Testing Multiple Verbs](#testing-multiple-verbs) - [Using Output from Previous Chapters](#using-output-from-previous-chapters) - [Managing Versions](#managing-versions) - [Managing Distributions](#managing-distributions) @@ -182,6 +183,18 @@ chapters: index: books ``` +### Testing Multiple Verbs + +Some APIs allow multiple verbs for the same effect. Specify multiple verbs as follows and the test tool will execute both. + +```yaml +- synopsis: Use POST and PUT interchangeably. + path: /{index} + method: + - POST + - PUT +``` + ### Using Output from Previous Chapters Consider the following chapters in [ml/model_groups](tests/plugins/ml/ml/model_groups.yaml) test story: diff --git a/json_schemas/test_story.schema.yaml b/json_schemas/test_story.schema.yaml index ac9529b30..95751843a 100644 --- a/json_schemas/test_story.schema.yaml +++ b/json_schemas/test_story.schema.yaml @@ -34,10 +34,16 @@ definitions: allOf: - $ref: '#/definitions/ChapterRequest' - properties: + method: + oneOf: + - type: array + items: + $ref: '#/definitions/HttpMethod' + - $ref: '#/definitions/HttpMethod' synopsis: type: string description: A brief description of the chapter. - pattern: ^\p{Lu}[\s\S]*\.$ + pattern: ^\p{Lu}[\s\S]*\.$|^\p{Lu}[\s\S]*\. \[(GET|PUT|POST|DELETE|PATCH|HEAD|OPTIONS)\]$ response: $ref: '#/definitions/ExpectedResponse' warnings: @@ -67,18 +73,21 @@ definitions: items: type: integer + HttpMethod: + type: string + # eslint-disable-next-line yml/sort-sequence-values + enum: [GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS] + ChapterRequest: type: object properties: - id: + id: type: string description: A unique identifier for the chapter, useful for accessing outputs. path: type: string method: - type: string - # eslint-disable-next-line yml/sort-sequence-values - enum: [GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS] + $ref: '#/definitions/HttpMethod' parameters: type: object additionalProperties: diff --git a/spec/namespaces/_core.yaml b/spec/namespaces/_core.yaml index 3b71f9287..a1c0e68b0 100644 --- a/spec/namespaces/_core.yaml +++ b/spec/namespaces/_core.yaml @@ -2482,16 +2482,15 @@ components: schema: type: object properties: - file: - type: string + id: + $ref: '../schemas/_common.yaml#/components/schemas/Id' params: description: |- Key-value pairs used to replace Mustache variables in the template. The key is the variable name. The value is the variable value. type: object - additionalProperties: - type: object + additionalProperties: true source: description: |- An inline search template. @@ -2710,7 +2709,7 @@ components: additionalProperties: type: object indices_boost: - description: Allows increasing the relevance of specific indices in the search. + description: Allows increasing the relevance of specific indexes in the search. type: object additionalProperties: type: number diff --git a/spec/namespaces/cat.yaml b/spec/namespaces/cat.yaml index 68fff5182..ca981bbd7 100644 --- a/spec/namespaces/cat.yaml +++ b/spec/namespaces/cat.yaml @@ -1068,63 +1068,63 @@ components: cat.aliases::query.expand_wildcards: in: query name: expand_wildcards - description: Whether to expand wildcard expression to concrete indexes that are open, closed or both. + description: Expands wildcard expressions to concrete indexes. Combine multiple values with commas. Supported values are `all`, `open`, `closed`, `hidden`, and `none`. schema: $ref: '../schemas/_common.yaml#/components/schemas/ExpandWildcards' style: form cat.aliases::query.format: name: format in: query - description: A short version of the Accept header (for example, `json`, `yaml`). + description: A short version of the `Accept` header, such as `json` or `yaml`. schema: type: string - description: A short version of the Accept header (for example, `json`, `yaml`). + description: A short version of the `Accept` header, such as `json` or `yaml`. cat.aliases::query.h: name: h in: query - description: Comma-separated list of column names to display. + description: A comma-separated list of column names to display. style: form schema: type: array items: type: string - description: Comma-separated list of column names to display. + description: A comma-separated list of column names to display. explode: true cat.aliases::query.help: name: help in: query - description: Return help information. + description: Returns help information. schema: - description: Return help information. + description: Returns help information. type: boolean default: false cat.aliases::query.local: name: local in: query - description: Return local information, do not retrieve the state from cluster-manager node. + description: Whether to return information from the local node only instead of from the cluster manager node. schema: type: boolean default: false - description: Return local information, do not retrieve the state from cluster-manager node. + description: Whether to return information from the local node only instead of from the cluster manager node. cat.aliases::query.s: name: s in: query - description: Comma-separated list of column names or column aliases to sort by. + description: A comma-separated list of column names or column aliases to sort by. style: form schema: type: array items: type: string - description: Comma-separated list of column names or column aliases to sort by. + description: A comma-separated list of column names or column aliases to sort by. explode: true cat.aliases::query.v: name: v in: query - description: Verbose mode. Display column headers. + description: Enables verbose mode, which displays column headers. schema: type: boolean default: false - description: Verbose mode. Display column headers. + description: Enables verbose mode, which displays column headers. cat.all_pit_segments::query.bytes: name: bytes in: query @@ -1179,7 +1179,7 @@ components: cat.allocation::path.node_id: in: path name: node_id - description: Comma-separated list of node identifiers or names used to limit the returned information. + description: A comma-separated list of node IDs or names used to limit the returned information. required: true schema: $ref: '../schemas/_common.yaml#/components/schemas/NodeIds' @@ -1187,55 +1187,55 @@ components: cat.allocation::query.bytes: in: query name: bytes - description: The unit used to display byte values. + description: The units used to display byte values. schema: $ref: '../schemas/_common.yaml#/components/schemas/ByteUnit' style: form cat.allocation::query.cluster_manager_timeout: name: cluster_manager_timeout in: query - description: Operation timeout for connection to cluster-manager node. + description: A timeout for connection to the cluster manager node. schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' x-version-added: '2.0' cat.allocation::query.format: name: format in: query - description: A short version of the Accept header (for example, `json`, `yaml`). + description: A short version of the HTTP `Accept` header, such as `json` or `yaml`. schema: type: string - description: A short version of the Accept header (for example, `json`, `yaml`). + description: A short version of the HTTP `Accept` header, such as `json` or `yaml`. cat.allocation::query.h: name: h in: query - description: Comma-separated list of column names to display. + description: A comma-separated list of column names to display. style: form schema: type: array items: type: string - description: Comma-separated list of column names to display. + description: A comma-separated list of column names to display. explode: true cat.allocation::query.help: name: help in: query - description: Return help information. + description: Returns help information. schema: type: boolean default: false - description: Return help information. + description: Returns help information. cat.allocation::query.local: name: local in: query - description: Return local information, do not retrieve the state from cluster-manager node. + description: Returns local information but does not retrieve the state from cluster-manager node. schema: type: boolean default: false - description: Return local information, do not retrieve the state from cluster-manager node. + description: Returns local information but does not retrieve the state from cluster-manager node. cat.allocation::query.master_timeout: name: master_timeout in: query - description: Operation timeout for connection to cluster-manager node. + description: A timeout for connection to the cluster manager node. schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' x-version-deprecated: '2.0' @@ -1244,46 +1244,46 @@ components: cat.allocation::query.s: name: s in: query - description: Comma-separated list of column names or column aliases to sort by. + description: A comma-separated list of column names or column aliases to sort by. style: form schema: type: array items: type: string - description: Comma-separated list of column names or column aliases to sort by. + description: A comma-separated list of column names or column aliases to sort by. explode: true cat.allocation::query.v: name: v in: query - description: Verbose mode. Display column headers. + description: Enables verbose mode, which displays column headers. schema: type: boolean default: false - description: Verbose mode. Display column headers. + description: Enables verbose mode, which displays column headers. cat.cluster_manager::query.cluster_manager_timeout: name: cluster_manager_timeout in: query - description: Operation timeout for connection to cluster-manager node. + description: A timeout for connection to the cluster manager node. schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' x-version-added: '2.0' cat.cluster_manager::query.format: name: format in: query - description: A short version of the Accept header (for example, `json`, `yaml`). + description: A short version of the HTTP `Accept` header, such as `json` or `yaml`. schema: type: string - description: A short version of the Accept header (for example, `json`, `yaml`). + description: A short version of the HTTP `Accept` header, such as `json` or `yaml`. cat.cluster_manager::query.h: name: h in: query - description: Comma-separated list of column names to display. + description: A comma-separated list of column names to display. style: form schema: type: array items: type: string - description: Comma-separated list of column names to display. + description: A comma-separated list of column names to display. explode: true cat.cluster_manager::query.help: name: help @@ -1304,7 +1304,7 @@ components: cat.cluster_manager::query.master_timeout: name: master_timeout in: query - description: Operation timeout for connection to cluster-manager node. + description: A timeout for connection to the cluster manager node. schema: $ref: '../schemas/_common.yaml#/components/schemas/Duration' x-version-deprecated: '2.0' @@ -1313,22 +1313,22 @@ components: cat.cluster_manager::query.s: name: s in: query - description: Comma-separated list of column names or column aliases to sort by. + description: A comma-separated list of column names or column aliases to sort by. style: form schema: type: array items: type: string - description: Comma-separated list of column names or column aliases to sort by. + description: A comma-separated list of column names or column aliases to sort by. explode: true cat.cluster_manager::query.v: name: v in: query - description: Verbose mode. Display column headers. + description: Enables verbose mode, which displays column headers. schema: type: boolean default: false - description: Verbose mode. Display column headers. + description: Enables verbose mode, which displays column headers. cat.count::path.index: in: path name: index diff --git a/spec/namespaces/indices.yaml b/spec/namespaces/indices.yaml index 90393c87f..ab17e1829 100644 --- a/spec/namespaces/indices.yaml +++ b/spec/namespaces/indices.yaml @@ -2115,6 +2115,7 @@ components: content: application/json: schema: + title: settings $ref: '../schemas/indices._common.yaml#/components/schemas/IndexSettings' required: true indices.put_template: @@ -2241,6 +2242,7 @@ components: content: application/json: schema: + title: template $ref: '../schemas/indices._common.yaml#/components/schemas/IndexTemplate' indices.split: content: @@ -2386,16 +2388,18 @@ components: $ref: '../schemas/_common.yaml#/components/schemas/ShardStatistics' backing_indices: description: Total number of backing indexes for the selected data streams. - type: number + type: integer + format: int32 data_stream_count: description: Total number of selected data streams. - type: number + type: integer + format: int32 data_streams: description: Contains statistics for the selected data streams. type: array items: - $ref: '../schemas/indices.data_streams_stats.yaml#/components/schemas/DataStreamsStatsItem' - total_store_sizes: + $ref: '../schemas/indices._common.yaml#/components/schemas/DataStreamStats' + total_store_size: $ref: '../schemas/_common.yaml#/components/schemas/HumanReadableByteCount' total_store_size_bytes: description: Total size, in bytes, of all shards for the selected data streams. @@ -4559,7 +4563,8 @@ components: Templates with lower 'order' values are merged first. Templates with higher 'order' values are merged later, overriding templates with lower values. schema: - type: number + type: integer + format: int32 style: form indices.recovery::path.index: in: path diff --git a/spec/namespaces/knn.yaml b/spec/namespaces/knn.yaml index 563f4f783..c547a7894 100644 --- a/spec/namespaces/knn.yaml +++ b/spec/namespaces/knn.yaml @@ -251,45 +251,28 @@ components: content: application/json: schema: - type: object - properties: - training_index: - type: string - training_field: - type: string - dimension: - type: integer - format: int32 - max_training_vector_count: - type: integer - format: int32 - search_size: - type: integer - format: int32 - description: - type: string - mode: - type: string - compression_level: - type: string - method: - type: string - spaceType: - type: string - required: - - dimension - - training_field - - training_index - required: true + $ref: '../schemas/knn._common.yaml#/components/schemas/TrainedModel' responses: - knn.delete_model@200: {} + knn.delete_model@200: + content: + application/json: + schema: + $ref: '../schemas/knn._common.yaml#/components/schemas/DeletedModel' knn.get_model@200: content: application/json: schema: type: object - knn.search_models@200: {} - knn.stats@200: {} + knn.search_models@200: + content: + application/json: + schema: + $ref: '../schemas/_core.search.yaml#/components/schemas/ResponseBody' + knn.stats@200: + content: + application/json: + schema: + $ref: '../schemas/knn._common.yaml#/components/schemas/Stats' knn.train_model@200: content: application/json: @@ -300,7 +283,11 @@ components: type: string required: - model_id - knn.warmup@200: {} + knn.warmup@200: + content: + application/json: + schema: + $ref: '../schemas/_common.yaml#/components/schemas/ShardsOperationResponseBase' parameters: knn.delete_model::path.model_id: name: model_id @@ -681,12 +668,19 @@ components: - graph_memory_usage_percentage - graph_query_errors - graph_query_requests + - graph_stats - hit_count - indexing_from_model_degraded - indices_in_cache - knn_query_requests + - knn_query_with_filter_requests - load_exception_count - load_success_count + - lucene_initialized + - max_distance_query_requests + - max_distance_query_with_filter_requests + - min_score_query_requests + - min_score_query_with_filter_requests - miss_count - model_index_status - nmslib_initialized diff --git a/spec/namespaces/ml.yaml b/spec/namespaces/ml.yaml index 700fc334f..67ec2460a 100644 --- a/spec/namespaces/ml.yaml +++ b/spec/namespaces/ml.yaml @@ -200,17 +200,77 @@ components: content: application/json: schema: - $ref: '../schemas/ml._common.yaml#/components/schemas/SearchModelsQuery' + type: object + properties: + query: + type: object + # TODO: Define the query schema + description: The query. + size: + type: integer + description: The number of models to return. + required: + - query + - size ml.create_connector: content: application/json: schema: - $ref: '../schemas/ml._common.yaml#/components/schemas/CreateConnectorRequest' + type: object + properties: + name: + type: string + description: + type: string + version: + type: integer + protocol: + type: string + credential: + $ref: '../schemas/ml._common.yaml#/components/schemas/Credential' + parameters: + $ref: '../schemas/ml._common.yaml#/components/schemas/Parameters' + client_config: + $ref: '../schemas/ml._common.yaml#/components/schemas/ClientConfig' + actions: + type: array + items: + $ref: '../schemas/ml._common.yaml#/components/schemas/Action' + required: + - actions + - credential + - description + - name + - parameters + - protocol + - version ml.register_agents: content: application/json: schema: - $ref: '../schemas/ml._common.yaml#/components/schemas/RegisterAgentsRequest' + type: object + properties: + name: + type: string + type: + type: string + description: + type: string + tools: + type: array + items: + $ref: '../schemas/ml._common.yaml#/components/schemas/ToolItems' + parameters: + $ref: '../schemas/ml._common.yaml#/components/schemas/Parameters' + app_type: + type: string + memory: + $ref: '../schemas/ml._common.yaml#/components/schemas/Memory' + llm: + $ref: '../schemas/ml._common.yaml#/components/schemas/LLM' + required: + - name + - type responses: ml.register_model_group@200: content: diff --git a/spec/namespaces/observability.yaml b/spec/namespaces/observability.yaml index 62cca3dfd..1b34a9241 100644 --- a/spec/namespaces/observability.yaml +++ b/spec/namespaces/observability.yaml @@ -8,8 +8,8 @@ paths: get: operationId: observability.get_localstats.0 x-operation-group: observability.get_localstats - x-version-added: '1.1' - description: Retrieves Local Stats of all observability objects. + x-version-added: '2.5' + description: Retrieves local stats of all observability objects. responses: '200': $ref: '#/components/responses/observability.get_localstats@200' @@ -177,7 +177,7 @@ components: observability.get_localstats@200: description: Retrieves content: - application/json: + text/plain: schema: type: string parameters: diff --git a/spec/namespaces/rollups.yaml b/spec/namespaces/rollups.yaml index e5e56a68a..5a5a70416 100644 --- a/spec/namespaces/rollups.yaml +++ b/spec/namespaces/rollups.yaml @@ -25,8 +25,8 @@ paths: externalDocs: url: https://opensearch.org/docs/latest/im-plugin/index-rollups/rollup-api/#create-or-update-an-index-rollup-job responses: - '200': - $ref: '#/components/responses/rollups.put@200' + '201': + $ref: '#/components/responses/rollups.put@201' parameters: - $ref: '#/components/parameters/rollups.put::path.id' - $ref: '#/components/parameters/rollups.put::query.if_primary_term' @@ -97,14 +97,26 @@ components: application/json: schema: $ref: '../schemas/rollups._common.yaml#/components/schemas/RollupEntity' - rollups.put@200: + rollups.put@201: content: application/json: schema: $ref: '../schemas/rollups._common.yaml#/components/schemas/RollupEntity' - rollups.delete@200: {} - rollups.start@200: {} - rollups.stop@200: {} + rollups.delete@200: + content: + application/json: + schema: + $ref: '../schemas/_common.yaml#/components/schemas/WriteResponseBase' + rollups.start@200: + content: + application/json: + schema: + $ref: '../schemas/_common.yaml#/components/schemas/AcknowledgedResponseBase' + rollups.stop@200: + content: + application/json: + schema: + $ref: '../schemas/_common.yaml#/components/schemas/AcknowledgedResponseBase' rollups.explain@200: content: application/json: diff --git a/spec/namespaces/snapshot.yaml b/spec/namespaces/snapshot.yaml index 1c4dc532c..f039049f2 100644 --- a/spec/namespaces/snapshot.yaml +++ b/spec/namespaces/snapshot.yaml @@ -478,7 +478,7 @@ components: snapshots: type: array items: - $ref: '../schemas/snapshot._common.yaml#/components/schemas/Status' + $ref: '../schemas/snapshot._common.yaml#/components/schemas/SnapshotStatus' required: - snapshots snapshot.verify_repository@200: diff --git a/spec/namespaces/transforms.yaml b/spec/namespaces/transforms.yaml index abc3da786..b67a31e2b 100644 --- a/spec/namespaces/transforms.yaml +++ b/spec/namespaces/transforms.yaml @@ -48,6 +48,8 @@ paths: requestBody: $ref: '#/components/requestBodies/transforms.put' responses: + '201': + $ref: '#/components/responses/transforms.put@201' '200': $ref: '#/components/responses/transforms.put@200' delete: @@ -120,17 +122,12 @@ components: content: application/json: schema: - type: object - properties: - transform: - $ref: '../schemas/transforms._common.yaml#/components/schemas/Transform' - required: - - transform + $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformRequest' transforms.put: content: application/json: schema: - $ref: '../schemas/transforms._common.yaml#/components/schemas/Transform' + $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformRequest' responses: transforms.search@200: content: @@ -141,13 +138,22 @@ components: content: application/json: schema: - $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformEntity' + $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformResponse' transforms.put@200: content: application/json: schema: - $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformEntity' - transforms.delete@200: {} + $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformResponse' + transforms.put@201: + content: + application/json: + schema: + $ref: '../schemas/transforms._common.yaml#/components/schemas/TransformResponse' + transforms.delete@200: + content: + application/json: + schema: + $ref: '../schemas/transforms._common.yaml#/components/schemas/DeleteTransformsResponse' transforms.start@200: content: application/json: diff --git a/spec/schemas/_common.yaml b/spec/schemas/_common.yaml index e28625a07..f5ab3b899 100644 --- a/spec/schemas/_common.yaml +++ b/spec/schemas/_common.yaml @@ -873,17 +873,29 @@ components: - nanos - s HealthStatus: - type: string - enum: - - green - - red - - yellow - HealthStatusCapitalized: - type: string - enum: - - GREEN - - RED - - YELLOW + # eslint-disable yml/sort-sequence-values + oneOf: + - description: All shards are assigned. + type: string + enum: + - green + - GREEN + - description: | + All primary shards are assigned, but one or more replica shards are + unassigned. If a node in the cluster fails, some data could be unavailable + until that node is repaired. + type: string + enum: + - yellow + - YELLOW + - description: | + One or more primary shards are unassigned, so some data is unavailable. This + can occur briefly during cluster startup as primary shards are assigned. + type: string + enum: + - red + - RED + # eslint-enable yml/sort-sequence-values ScheduleTimeOfDay: description: A time of day, expressed either as `hh:mm`, `noon`, `midnight`, or an hour/minutes structure. oneOf: diff --git a/spec/schemas/indices._common.yaml b/spec/schemas/indices._common.yaml index a8dee812f..bfe248437 100644 --- a/spec/schemas/indices._common.yaml +++ b/spec/schemas/indices._common.yaml @@ -988,7 +988,8 @@ components: type: boolean generation: description: Current generation for the data stream. This number acts as a cumulative count of the stream's rollovers, starting at 1. - type: number + type: integer + format: int64 hidden: description: If `true`, the data stream is hidden. type: boolean @@ -1012,7 +1013,7 @@ components: description: If `true`, the data stream is created and managed by cross-cluster replication and the local cluster can not write into this data stream or change its mappings. type: boolean status: - $ref: '_common.yaml#/components/schemas/HealthStatusCapitalized' + $ref: '_common.yaml#/components/schemas/HealthStatus' system: description: If `true`, the data stream is created and managed by an OpenSearch stack component and cannot be modified through normal user interaction. type: boolean @@ -1231,3 +1232,24 @@ components: additionalProperties: title: metadata description: Additional details about the error. + DataStreamStats: + type: object + properties: + backing_indices: + description: Current number of backing indexes for the data stream. + type: integer + format: int32 + data_stream: + $ref: '_common.yaml#/components/schemas/Name' + maximum_timestamp: + $ref: '_common.yaml#/components/schemas/EpochTimeUnitMillis' + store_size: + $ref: '_common.yaml#/components/schemas/HumanReadableByteCount' + store_size_bytes: + description: Total size, in bytes, of all shards for the data stream's backing indexes. + $ref: '_common.yaml#/components/schemas/ByteCount' + required: + - backing_indices + - data_stream + - maximum_timestamp + - store_size_bytes diff --git a/spec/schemas/indices.data_streams_stats.yaml b/spec/schemas/indices.data_streams_stats.yaml deleted file mode 100644 index 216fa77ea..000000000 --- a/spec/schemas/indices.data_streams_stats.yaml +++ /dev/null @@ -1,28 +0,0 @@ -openapi: 3.1.0 -info: - title: Schemas of `indices.data_streams_stats` Category - description: Schemas of `indices.data_streams_stats` category. - version: 1.0.0 -paths: {} -components: - schemas: - DataStreamsStatsItem: - type: object - properties: - backing_indices: - description: Current number of backing indexes for the data stream. - type: number - data_stream: - $ref: '_common.yaml#/components/schemas/Name' - maximum_timestamp: - $ref: '_common.yaml#/components/schemas/EpochTimeUnitMillis' - store_size: - $ref: '_common.yaml#/components/schemas/HumanReadableByteCount' - store_size_bytes: - description: Total size, in bytes, of all shards for the data stream's backing indexes. - $ref: '_common.yaml#/components/schemas/ByteCount' - required: - - backing_indices - - data_stream - - maximum_timestamp - - store_size_bytes diff --git a/spec/schemas/indices.shard_stores.yaml b/spec/schemas/indices.shard_stores.yaml index 0745101db..9b76b9027 100644 --- a/spec/schemas/indices.shard_stores.yaml +++ b/spec/schemas/indices.shard_stores.yaml @@ -40,6 +40,8 @@ components: $ref: '_common.yaml#/components/schemas/Id' store_exception: $ref: '#/components/schemas/ShardStoreException' + additionalProperties: + $ref: '_common.yaml#/components/schemas/NodeAttributes' required: - allocation ShardStoreAllocation: diff --git a/spec/schemas/knn._common.yaml b/spec/schemas/knn._common.yaml index b4cbeaa36..3efddc192 100644 --- a/spec/schemas/knn._common.yaml +++ b/spec/schemas/knn._common.yaml @@ -25,3 +25,164 @@ components: - always - missing - popular + Stats: + type: object + properties: + _nodes: + $ref: '_common.yaml#/components/schemas/NodeStatistics' + cluster_name: + $ref: '_common.yaml#/components/schemas/Name' + circuit_breaker_triggered: + type: boolean + model_index_status: + oneOf: + - $ref: '_common.yaml#/components/schemas/HealthStatus' + - type: 'null' + nodes: + type: object + additionalProperties: + $ref: '#/components/schemas/NodeStats' + NodeStats: + type: object + properties: + max_distance_query_with_filter_requests: + type: number + graph_memory_usage_percentage: + $ref: '_common.yaml#/components/schemas/PercentageNumber' + graph_query_requests: + type: number + graph_memory_usage: + type: number + cache_capacity_reached: + type: boolean + load_success_count: + type: number + training_memory_usage: + type: number + indices_in_cache: + type: object + additionalProperties: true + script_query_errors: + type: number + hit_count: + type: number + knn_query_requests: + type: number + total_load_time: + type: number + miss_count: + type: number + min_score_query_requests: + type: number + knn_query_with_filter_requests: + type: number + training_memory_usage_percentage: + $ref: '_common.yaml#/components/schemas/PercentageNumber' + max_distance_query_requests: + type: number + lucene_initialized: + type: boolean + graph_index_requests: + type: number + faiss_initialized: + type: boolean + load_exception_count: + type: number + training_errors: + type: number + min_score_query_with_filter_requests: + type: number + eviction_count: + type: number + nmslib_initialized: + type: boolean + script_compilations: + type: number + script_query_requests: + type: number + graph_stats: + $ref: '#/components/schemas/GraphStats' + graph_query_errors: + type: number + indexing_from_model_degraded: + type: boolean + graph_index_errors: + type: number + training_requests: + type: number + script_compilation_errors: + type: number + GraphStats: + type: object + properties: + merge: + $ref: '#/components/schemas/GraphMergeStats' + refresh: + $ref: '#/components/schemas/GraphRefreshStats' + GraphRefreshStats: + type: object + properties: + total: + type: number + total_time_in_millis: + $ref: '_common.yaml#/components/schemas/DurationValueUnitMillis' + GraphMergeStats: + type: object + properties: + current: + type: number + total: + type: number + total_time_in_millis: + $ref: '_common.yaml#/components/schemas/DurationValueUnitMillis' + current_docs: + type: number + total_docs: + type: number + total_size_in_bytes: + $ref: '_common.yaml#/components/schemas/ByteCount' + current_size_in_bytes: + $ref: '_common.yaml#/components/schemas/ByteCount' + TrainedModel: + type: object + properties: + training_index: + type: string + training_field: + type: string + dimension: + type: integer + format: int32 + max_training_vector_count: + type: integer + format: int32 + search_size: + type: integer + format: int32 + description: + type: string + mode: + type: string + compression_level: + type: string + method: + type: string + spaceType: + type: string + required: + - dimension + - training_field + - training_index + DeletedModel: + type: object + properties: + model_id: + type: string + result: + type: string + enum: + - deleted + - error + required: + - model_id + - result diff --git a/spec/schemas/ml._common.yaml b/spec/schemas/ml._common.yaml index ffe3b7bf3..1b83fcfce 100644 --- a/spec/schemas/ml._common.yaml +++ b/spec/schemas/ml._common.yaml @@ -6,19 +6,6 @@ info: paths: {} components: schemas: - SearchModelsQuery: - type: object - properties: - query: - type: object - # TODO: Define the query schema - description: The query. - size: - type: integer - description: The number of models to return. - required: - - query - - size SearchModelsResponse: type: object properties: @@ -291,35 +278,6 @@ components: UndeployModelNodeStats: type: object additionalProperties: true - CreateConnectorRequest: - type: object - properties: - name: - type: string - description: - type: string - version: - type: integer - protocol: - type: string - credential: - $ref: '#/components/schemas/Credential' - parameters: - $ref: '#/components/schemas/Parameters' - client_config: - $ref: '#/components/schemas/ClientConfig' - actions: - type: array - items: - $ref: '#/components/schemas/Action' - required: - - actions - - credential - - description - - name - - parameters - - protocol - - version Credential: type: object properties: @@ -367,30 +325,6 @@ components: type: string post_process_function: type: string - RegisterAgentsRequest: - type: object - properties: - name: - type: string - type: - type: string - description: - type: string - tools: - type: array - items: - $ref: '#/components/schemas/ToolItems' - parameters: - $ref: '#/components/schemas/Parameters' - app_type: - type: string - memory: - $ref: '#/components/schemas/Memory' - llm: - $ref: '#/components/schemas/LLM' - required: - - name - - type LLM: type: object properties: diff --git a/spec/schemas/observability._common.yaml b/spec/schemas/observability._common.yaml index 08b7fbce6..bc7c56d87 100644 --- a/spec/schemas/observability._common.yaml +++ b/spec/schemas/observability._common.yaml @@ -44,7 +44,6 @@ components: $ref: '#/components/schemas/SavedQuery' required: - objectId - - tenant OperationalPanel: type: object diff --git a/spec/schemas/rollups._common.yaml b/spec/schemas/rollups._common.yaml index 6e6da5704..b67cc0938 100644 --- a/spec/schemas/rollups._common.yaml +++ b/spec/schemas/rollups._common.yaml @@ -10,11 +10,14 @@ components: type: object properties: _id: - type: string - _seqNo: - type: number - _primaryTerm: - type: number + $ref: '_common.yaml#/components/schemas/Id' + _seq_no: + $ref: '_common.yaml#/components/schemas/SequenceNumber' + _primary_term: + type: integer + format: int64 + _version: + $ref: '_common.yaml#/components/schemas/VersionNumber' rollup: $ref: '#/components/schemas/Rollup' Rollup: @@ -39,7 +42,7 @@ components: target_index: type: string metadata_id: - type: string + type: ['null', string] page_size: type: number delay: @@ -59,18 +62,15 @@ components: description: 'Set up a Mustache message template for error notifications. For example, if an index rollup job fails, the system sends a message to a Slack channel.' ExplainEntities: type: object - properties: - item: - $ref: '#/components/schemas/Explain' + additionalProperties: + $ref: '#/components/schemas/Explain' Explain: type: object properties: - rollup_id: - type: string - last_updated_time: - type: number - continuous: - $ref: '#/components/schemas/Continuous' + metadata_id: + type: ['null', string] + rollup_metadata: + type: ['null', object] DimensionsConfigItem: type: object properties: @@ -95,6 +95,8 @@ components: type: string target_field: type: string + format: + type: ['null', string] HistogramDimension: type: object properties: @@ -166,30 +168,3 @@ components: - $ref: '#/components/schemas/Cron' schedule_delay: type: number - Continuous: - type: object - properties: - next_window_start_time: - type: number - next_window_end_time: - type: number - status: - type: string - failure_reason: - type: string - stats: - $ref: '#/components/schemas/Stats' - Stats: - type: object - properties: - pages_processed: - type: number - documents_processed: - type: number - rollups_indexed: - type: number - index_time_in_ms: - type: number - search_time_in_ms: - type: number - diff --git a/spec/schemas/snapshot._common.yaml b/spec/schemas/snapshot._common.yaml index 8eae7a0ea..d21823669 100644 --- a/spec/schemas/snapshot._common.yaml +++ b/spec/schemas/snapshot._common.yaml @@ -95,22 +95,16 @@ components: chunk_size: type: string compress: - oneOf: - - type: string - - type: boolean + $ref: '_common.yaml#/components/schemas/StringifiedBoolean' concurrent_streams: - oneOf: - - type: string - - type: number + $ref: '_common.yaml#/components/schemas/StringifiedInteger' location: type: string read_only: - oneOf: - - type: string - - type: boolean + $ref: '_common.yaml#/components/schemas/StringifiedBoolean' required: - location - Status: + SnapshotStatus: type: object properties: include_global_state: @@ -122,7 +116,7 @@ components: repository: type: string shards_stats: - $ref: '#/components/schemas/ShardsStats' + $ref: '#/components/schemas/SnapshotShardsStats' snapshot: type: string state: @@ -132,7 +126,6 @@ components: uuid: $ref: '_common.yaml#/components/schemas/Uuid' required: - - include_global_state - indices - repository - shards_stats @@ -148,7 +141,7 @@ components: additionalProperties: $ref: '#/components/schemas/SnapshotShardsStatus' shards_stats: - $ref: '#/components/schemas/ShardsStats' + $ref: '#/components/schemas/SnapshotShardsStats' stats: $ref: '#/components/schemas/SnapshotStats' required: @@ -158,28 +151,43 @@ components: SnapshotShardsStatus: type: object properties: + node: + $ref: '_common.yaml#/components/schemas/NodeId' + reason: + type: string stage: - $ref: '#/components/schemas/ShardsStatsStage' + $ref: '#/components/schemas/SnapshotShardsStatsStage' stats: - $ref: '#/components/schemas/ShardsStatsSummary' + $ref: '#/components/schemas/SnapshotShardsStatsSummary' required: - stage - stats - ShardsStatsStage: - type: string - enum: - - DONE - - FAILURE - - FINALIZE - - INIT - - STARTED - ShardsStatsSummary: + SnapshotShardsStatsStage: + oneOf: + - type: string + const: DONE + description: Number of shards in the snapshot that were successfully stored in the repository. + - type: string + const: FAILURE + description: Number of shards in the snapshot that were not successfully stored in the repository. + - type: string + const: FINALIZE + description: Number of shards in the snapshot that are in the finalizing stage of being stored in the repository. + - type: string + const: INIT + description: Number of shards in the snapshot that are in the initializing stage of being stored in the repository. + - type: string + const: STARTED + description: Number of shards in the snapshot that are in the started stage of being stored in the repository. + SnapshotShardsStatsSummary: type: object properties: incremental: - $ref: '#/components/schemas/ShardsStatsSummaryItem' + $ref: '#/components/schemas/SnapshotShardsStatsSummaryItem' + processed: + $ref: '#/components/schemas/SnapshotShardsStatsSummaryItem' total: - $ref: '#/components/schemas/ShardsStatsSummaryItem' + $ref: '#/components/schemas/SnapshotShardsStatsSummaryItem' start_time_in_millis: $ref: '_common.yaml#/components/schemas/EpochTimeUnitMillis' time: @@ -191,31 +199,38 @@ components: - start_time_in_millis - time_in_millis - total - ShardsStatsSummaryItem: + SnapshotShardsStatsSummaryItem: type: object properties: file_count: - type: number + type: integer + format: int64 size_in_bytes: $ref: '_common.yaml#/components/schemas/ByteCount' required: - file_count - size_in_bytes - ShardsStats: + SnapshotShardsStats: type: object properties: done: - type: number + type: integer + format: int64 failed: - type: number + type: integer + format: int64 finalizing: - type: number + type: integer + format: int64 initializing: - type: number + type: integer + format: int64 started: - type: number + type: integer + format: int64 total: - type: number + type: integer + format: int64 required: - done - failed @@ -228,6 +243,8 @@ components: properties: incremental: $ref: '#/components/schemas/FileCountSnapshotStats' + processed: + $ref: '#/components/schemas/FileCountSnapshotStats' start_time_in_millis: $ref: '_common.yaml#/components/schemas/EpochTimeUnitMillis' time: @@ -245,7 +262,8 @@ components: type: object properties: file_count: - type: number + type: integer + format: int32 size_in_bytes: $ref: '_common.yaml#/components/schemas/ByteCount' required: diff --git a/spec/schemas/transforms._common.yaml b/spec/schemas/transforms._common.yaml index bee81dc00..b51793236 100644 --- a/spec/schemas/transforms._common.yaml +++ b/spec/schemas/transforms._common.yaml @@ -5,15 +5,25 @@ info: version: 1.0.0 components: schemas: - TransformEntity: + TransformRequest: + type: object + properties: + transform: + $ref: '#/components/schemas/Transform' + required: + - transform + TransformResponse: type: object properties: _id: - type: string - _seqNo: - type: number - _primaryTerm: - type: number + $ref: '_common.yaml#/components/schemas/Id' + _primary_term: + type: integer + format: int64 + _seq_no: + $ref: '_common.yaml#/components/schemas/SequenceNumber' + _version: + $ref: '_common.yaml#/components/schemas/VersionNumber' transform: $ref: '#/components/schemas/Transform' TransformsResponse: @@ -24,7 +34,58 @@ components: transforms: type: array items: - $ref: '#/components/schemas/TransformEntity' + $ref: '#/components/schemas/TransformResponse' + DeleteTransformsResponse: + type: object + properties: + took: + type: integer + format: int64 + errors: + type: boolean + items: + type: array + items: + $ref: '#/components/schemas/DeleteTransformResponseItem' + DeleteTransformResponseItem: + type: object + properties: + delete: + $ref: '#/components/schemas/DeletedTransformResponse' + DeletedTransformResponse: + type: object + properties: + _type: + $ref: '_common.yaml#/components/schemas/Type' + _index: + type: string + _id: + $ref: '_common.yaml#/components/schemas/Id' + _version: + $ref: '_common.yaml#/components/schemas/VersionNumber' + result: + type: string + forced_refresh: + type: boolean + _shards: + $ref: '_common.yaml#/components/schemas/ShardStatistics' + _primary_term: + type: integer + format: int64 + _seq_no: + $ref: '_common.yaml#/components/schemas/SequenceNumber' + status: + type: number + required: + - _id + - _index + - _primary_term + - _seq_no + - _shards + - _version + - forced_refresh + - result + - status Transform: type: object properties: @@ -37,13 +98,13 @@ components: schedule: $ref: '#/components/schemas/Schedule' metadata_id: - type: string + type: ['null', string] updated_at: - type: string + type: number enabled: type: boolean enabled_at: - type: number + type: ['null', number] description: type: string source_index: @@ -63,9 +124,9 @@ components: items: $ref: '#/components/schemas/GroupsConfigItem' aggregations: - type: array - items: - $ref: '#/components/schemas/MetricsConfigItem' + type: object + additionalProperties: + $ref: '#/components/schemas/GroupsConfigItem' ExplainResponse: type: object additionalProperties: @@ -74,9 +135,11 @@ components: type: object properties: metadata_id: - type: string + type: ['null', string] transform_metadata: - $ref: '#/components/schemas/TransformMetadata' + oneOf: + - $ref: '#/components/schemas/TransformMetadata' + - type: 'null' Preview: type: object properties: @@ -140,32 +203,6 @@ components: type: string target_field: type: string - MetricsConfigItem: - type: object - properties: - source_field: - type: string - target_field: - type: string - metrics: - type: array - items: - $ref: '#/components/schemas/MetricsConfigMetrics' - MetricsConfigMetrics: - type: object - properties: - avg: - type: object - sum: - type: object - max: - type: object - min: - type: object - value_count: - type: object - minProperties: 1 - maxProperties: 1 ContinuousStats: type: object properties: diff --git a/tests/default/_core/render/template.yaml b/tests/default/_core/render/template.yaml new file mode 100644 index 000000000..995929c72 --- /dev/null +++ b/tests/default/_core/render/template.yaml @@ -0,0 +1,62 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test rendering a search template as a search query. + +prologues: + - path: /_scripts/movie_template + method: POST + request: + content_type: application/json + payload: + script: + lang: mustache + source: > + { + "query": { + "match": { + "{{field}}": "{{value}}" + } + } + } +epilogues: + - path: /_scripts/movie_template + method: DELETE + status: [200, 404] +chapters: + - synopsis: Render the movie template (request payload). + path: /_render/template + method: + - GET + - POST + request: + payload: + id: movie_template + params: + field: director + value: Quentin Tarantino + response: + status: 200 + payload: + template_output: + query: + match: + director: Quentin Tarantino + - synopsis: Render the movie template (path). + path: /_render/template/{id} + method: + - GET + - POST + parameters: + id: movie_template + request: + payload: + params: + field: director + value: Christopher Nolan + response: + status: 200 + payload: + template_output: + query: + match: + director: Christopher Nolan diff --git a/tests/default/_core/settings.yaml b/tests/default/_core/settings.yaml new file mode 100644 index 000000000..919b88fcc --- /dev/null +++ b/tests/default/_core/settings.yaml @@ -0,0 +1,58 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test settings. +prologues: + - path: /movies + method: PUT + - path: /movies/_settings + method: PUT + request: + payload: + settings: + index.blocks.write: true + index: + number_of_replicas: 4 +epilogues: + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Get settings. + distributions: + excluded: + - amazon-managed + - amazon-serverless + path: /_settings + method: GET + parameters: + allow_no_indices: true + expand_wildcards: all + flat_settings: true + include_defaults: true + ignore_unavailable: true + local: true + response: + status: 200 + - synopsis: Get settings (cluster_manager_timeout). + distributions: + excluded: + - amazon-managed + - amazon-serverless + path: /_settings + method: GET + version: '>= 2.0' + parameters: + allow_no_indices: true + expand_wildcards: all + flat_settings: true + include_defaults: true + ignore_unavailable: true + local: true + cluster_manager_timeout: 1s + response: + status: 200 + - synopsis: Get settings by name. + path: /_settings/{name} + method: GET + parameters: + name: index.number_of_replicas \ No newline at end of file diff --git a/tests/default/_core/shard_stores.yaml b/tests/default/_core/shard_stores.yaml new file mode 100644 index 000000000..d425f9b73 --- /dev/null +++ b/tests/default/_core/shard_stores.yaml @@ -0,0 +1,26 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test _shard_stores API. +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: movies, _id: movie1}} + - {director: Quentin Tarantino, title: Pulp Fiction, year: 1994} + - {create: {_index: movies, _id: movie2}} + - {director: Christopher Nolan, title: Inception, year: 2010} +epilogues: + - path: /movies + method: DELETE + status: [200, 404] + +chapters: + - synopsis: Verify shard store information across all indices. + path: /_shard_stores + method: GET + response: + status: 200 diff --git a/tests/default/indices/settings.yaml b/tests/default/indices/settings.yaml index 8513ffaa6..0ac83462b 100644 --- a/tests/default/indices/settings.yaml +++ b/tests/default/indices/settings.yaml @@ -10,40 +10,6 @@ epilogues: method: DELETE status: [200, 404] chapters: - - synopsis: Get global settings. - distributions: - excluded: - - amazon-managed - - amazon-serverless - path: /_settings - method: GET - parameters: - allow_no_indices: true - expand_wildcards: all - flat_settings: true - include_defaults: true - ignore_unavailable: true - local: true - response: - status: 200 - - synopsis: Get global settings (cluster_manager_timeout). - distributions: - excluded: - - amazon-managed - - amazon-serverless - path: /_settings - method: GET - version: '>= 2.0' - parameters: - allow_no_indices: true - expand_wildcards: all - flat_settings: true - include_defaults: true - ignore_unavailable: true - local: true - cluster_manager_timeout: 1s - response: - status: 200 - synopsis: Write a setting to an index. path: /{index}/_settings method: PUT diff --git a/tests/default/indices/shard_stores.yaml b/tests/default/indices/shard_stores.yaml new file mode 100644 index 000000000..07ca4f8fc --- /dev/null +++ b/tests/default/indices/shard_stores.yaml @@ -0,0 +1,28 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test _shard_stores API. +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: movies, _id: movie1}} + - {director: Quentin Tarantino, title: Pulp Fiction, year: 1994} + - {create: {_index: movies, _id: movie2}} + - {director: Christopher Nolan, title: Inception, year: 2010} +epilogues: + - path: /movies + method: DELETE + status: [200, 404] + +chapters: + - synopsis: Verify shard store information for a specific index. + path: /{index}/_shard_stores + parameters: + index: movies + method: GET + response: + status: 200 diff --git a/tests/default/knn/models.yaml b/tests/default/knn/models.yaml new file mode 100644 index 000000000..3589fc162 --- /dev/null +++ b/tests/default/knn/models.yaml @@ -0,0 +1,61 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Delete a k-NN model. +version: '>= 2.17' +prologues: + - method: PUT + path: /movies + request: + payload: + settings: + index: + knn: true + mappings: + properties: + recommendation_vector: + type: knn_vector + dimension: 8 + status: [200] + - method: POST + path: /_bulk + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {index: {_index: movies, _id: '1'}} + - {recommendation_vector: [1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5], duration: 12.2} + - {index: {_index: movies, _id: '2'}} + - {recommendation_vector: [2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5], duration: 7.1} + status: [200] + - method: POST + path: /_plugins/_knn/models/model-1/_train + request: + payload: + training_index: movies + training_field: recommendation_vector + dimension: 8 + max_training_vector_count: 1200 + search_size: 100 + description: Test model + mode: on_disk + compression_level: 32x + spaceType: l2 + status: [200] +epilogues: + - path: /movies + method: DELETE + status: [200, 404] + - path: /_plugins/_knn/models/model-1 + method: DELETE + status: [200, 404] +chapters: + - synopsis: Delete a trained model. + method: DELETE + path: /_plugins/_knn/models/{model_id} + parameters: + model_id: model-1 + retry: + count: 3 + response: + status: 200 diff --git a/tests/default/knn/models/search.yaml b/tests/default/knn/models/search.yaml new file mode 100644 index 000000000..52711b0f5 --- /dev/null +++ b/tests/default/knn/models/search.yaml @@ -0,0 +1,70 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Search for a k-NN model. +version: '>= 2.17' +prologues: + - method: PUT + path: /movies + request: + payload: + settings: + index: + knn: true + mappings: + properties: + recommendation_vector: + type: knn_vector + dimension: 8 + status: [200] + - method: POST + path: /_bulk + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {index: {_index: movies, _id: '1'}} + - {recommendation_vector: [1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5], duration: 12.2} + - {index: {_index: movies, _id: '2'}} + - {recommendation_vector: [2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5], duration: 7.1} + status: [200] + - method: POST + path: /_plugins/_knn/models/model-1/_train + request: + payload: + training_index: movies + training_field: recommendation_vector + dimension: 8 + max_training_vector_count: 1200 + search_size: 100 + description: Test model + mode: on_disk + compression_level: 32x + spaceType: l2 + status: [200] +epilogues: + - path: /movies + method: DELETE + status: [200, 404] + - path: /_plugins/_knn/models/model-1 + method: DELETE + status: [200, 404] +chapters: + - synopsis: Search models (GET). + method: GET + path: /_plugins/_knn/models/_search + request: + payload: + query: + match_all: {} + response: + status: 200 + - synopsis: Search models (POST). + method: POST + path: /_plugins/_knn/models/_search + request: + payload: + query: + match_all: {} + response: + status: 200 diff --git a/tests/default/knn/train_model.yaml b/tests/default/knn/models/train.yaml similarity index 77% rename from tests/default/knn/train_model.yaml rename to tests/default/knn/models/train.yaml index cb58ed97c..fbcfb19d5 100644 --- a/tests/default/knn/train_model.yaml +++ b/tests/default/knn/models/train.yaml @@ -1,8 +1,7 @@ -$schema: ../../../json_schemas/test_story.schema.yaml +$schema: ../../../../json_schemas/test_story.schema.yaml -description: Test training k-NN model with disk-based parameters. +description: Train a k-NN model with disk-based parameters. version: '>= 2.17' - prologues: - method: PUT path: /movies @@ -19,6 +18,8 @@ prologues: status: [200] - method: POST path: /_bulk + parameters: + refresh: true request: content_type: application/x-ndjson payload: @@ -41,9 +42,6 @@ prologues: - {index: {_index: movies, _id: '9'}} - {recommendation_vector: [9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5, 9.5], duration: 8.9} status: [200] - - method: POST - path: /_refresh - status: [200] epilogues: - path: /movies method: DELETE @@ -53,9 +51,11 @@ epilogues: model_id: ${train_model.test_model_id} method: DELETE status: [200, 404] - + - path: /_plugins/_knn/models/model-1 + method: DELETE + status: [200, 404] chapters: - - synopsis: Test training a model with disk-based parameters. + - synopsis: Train a new model with disk-based parameters. id: train_model method: POST path: /_plugins/_knn/models/_train @@ -88,3 +88,21 @@ chapters: payload: model_id: ${train_model.test_model_id} state: created + - synopsis: Train a model with a given ID. + method: POST + path: /_plugins/_knn/models/{model_id}/_train + parameters: + model_id: model-1 + request: + payload: + training_index: movies + training_field: recommendation_vector + dimension: 8 + max_training_vector_count: 2400 + search_size: 100 + description: Test model + mode: on_disk + compression_level: 32x + spaceType: l2 + response: + status: 200 diff --git a/tests/default/knn/stats.yaml b/tests/default/knn/stats.yaml new file mode 100644 index 000000000..f1fb9c5c9 --- /dev/null +++ b/tests/default/knn/stats.yaml @@ -0,0 +1,35 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Get k-NN stats. +chapters: + - synopsis: Get k-NN stats. + method: GET + path: /_plugins/_knn/stats + response: + status: 200 + - synopsis: Get k-NN stat. + method: GET + path: /_plugins/_knn/stats/{stat} + parameters: + stat: + - hit_count + response: + status: 200 + - synopsis: Get all k-NN stat for all nodes. + method: GET + path: /_plugins/_knn/{node_id}/stats + parameters: + node_id: + - _all + response: + status: 200 + - synopsis: Get `hit_count` k-NN stat for all nodes. + method: GET + path: /_plugins/_knn/{node_id}/stats/{stat} + parameters: + node_id: + - _all + stat: + - hit_count + response: + status: 200 diff --git a/tests/default/knn/warmup.yaml b/tests/default/knn/warmup.yaml new file mode 100644 index 000000000..656e67ee6 --- /dev/null +++ b/tests/default/knn/warmup.yaml @@ -0,0 +1,30 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Load all the native library files for all the shards into native memory. +prologues: + - method: PUT + path: /movies + request: + payload: + settings: + index: + knn: true + mappings: + properties: + recommendation_vector: + type: knn_vector + dimension: 8 + status: [200] +epilogues: + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Warmup an index. + method: GET + path: /_plugins/_knn/warmup/{index} + parameters: + index: + - movies + response: + status: 200 diff --git a/tests/default/notifications/configs.yaml b/tests/default/notifications/configs.yaml index 3a1523f1e..b89256418 100644 --- a/tests/default/notifications/configs.yaml +++ b/tests/default/notifications/configs.yaml @@ -91,7 +91,33 @@ chapters: config_id: webhook-configuration response: status: 200 + - synopsis: Create another channel configuration. + path: /_plugins/_notifications/configs + method: POST + request: + payload: + config_id: second-configuration + config: + name: Notifications Channel + description: Second notifications channel. + config_type: webhook + is_enabled: true + webhook: + url: http://webhook:8080/ + response: + status: 200 + - synopsis: Delete multiple channel configurations. + version: '>= 2.2' + path: /_plugins/_notifications/configs + method: DELETE + parameters: + config_id_list: second-configuration + response: + status: 200 epilogues: - path: /_plugins/_notifications/configs/webhook-configuration method: DELETE status: [200,404] + - path: /_plugins/_notifications/configs/second-configuration + method: DELETE + status: [200,404] diff --git a/tests/default/observability/local/stats.yaml b/tests/default/observability/local/stats.yaml new file mode 100644 index 000000000..024cfdf02 --- /dev/null +++ b/tests/default/observability/local/stats.yaml @@ -0,0 +1,12 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test backend stats. +version: '>= 2.5' + +chapters: + - synopsis: Get observability stats. + path: /_plugins/_observability/_local/stats + method: GET + response: + status: 200 + content_type: text/plain \ No newline at end of file diff --git a/tests/default/observability/observability.yaml b/tests/default/observability/object.yaml similarity index 66% rename from tests/default/observability/observability.yaml rename to tests/default/observability/object.yaml index e27b2be5e..cb0f08846 100644 --- a/tests/default/observability/observability.yaml +++ b/tests/default/observability/object.yaml @@ -2,13 +2,9 @@ $schema: ../../../json_schemas/test_story.schema.yaml description: Test various operations of the OpenSearch Observability Object API. -prologues: - - path: /_plugins/_observability/object/{object_id} - method: DELETE - parameters: - object_id: test_object - status: [200, 404] - - path: /_plugins/_observability/object +chapters: + - synopsis: Create an observability object. + path: /_plugins/_observability/object method: POST request: payload: @@ -56,12 +52,12 @@ prologues: tokens: - name: field1 type: text - status: [200] - - path: /_refresh + - synopsis: Refresh index. + path: /_refresh method: POST - status: [200] -chapters: - - synopsis: Retrieve specific Observability object after creation. + warnings: + multiple-paths-detected: false + - synopsis: Retrieve an observability object. path: /_plugins/_observability/object/{object_id} id: observatory_object method: GET @@ -74,7 +70,7 @@ chapters: totalHits: 1 totalHitRelation: eq observabilityObjectList: [] - - synopsis: Update specific Observability object. + - synopsis: Update an observability object. path: /_plugins/_observability/object/{object_id} method: PUT parameters: @@ -82,7 +78,6 @@ chapters: request: payload: objectId: test_object - tenant: '' operationalPanel: name: updated_test_panel visualizations: [] @@ -128,11 +123,9 @@ chapters: status: 200 payload: objectId: test_object - - synopsis: Retrieve specific Observability object after update. - path: /_plugins/_observability/object/{object_id} + - synopsis: Retrieve all observability objects. + path: /_plugins/_observability/object method: GET - parameters: - object_id: test_object response: status: 200 payload: @@ -140,16 +133,65 @@ chapters: totalHits: 1 totalHitRelation: eq observabilityObjectList: [] - - synopsis: Retrieve list of Observability objects. + - synopsis: Delete an observability object. + path: /_plugins/_observability/object/{object_id} + method: DELETE + parameters: + object_id: test_object + - synopsis: Create another observability object. path: /_plugins/_observability/object - method: GET - response: - status: 200 + method: POST + request: payload: - startIndex: 0 - totalHits: 1 - totalHitRelation: eq - observabilityObjectList: [] + objectId: test_object + operationalPanel: + name: test_panel + visualizations: [] + timeRange: + from: now-1h + to: now + queryFilter: + query: '' + language: ppl + applicationId: test_app + savedVisualization: + name: viz1 + description: desc1 + query: '' + type: line + selected_date_range: + start: now-1d + end: now + text: Last 24 hours + selected_timestamp: + name: timestamp + type: time + selected_fields: + text: field1 + tokens: + - name: field1 + type: text + savedQuery: + name: query1 + description: desc1 + query: '' + selected_date_range: + start: now-1d + end: now + text: Last 24 hours + selected_timestamp: + name: timestamp + type: time + selected_fields: + text: field1 + tokens: + - name: field1 + type: text + - synopsis: Delete an observability object (query string). + path: /_plugins/_observability/object + method: DELETE + parameters: + objectId: test_object epilogues: - path: /_plugins/_observability/object/{object_id} method: DELETE diff --git a/tests/default/ppl/stats.yaml b/tests/default/ppl/stats.yaml index fafba95ec..eba2073f3 100644 --- a/tests/default/ppl/stats.yaml +++ b/tests/default/ppl/stats.yaml @@ -3,7 +3,13 @@ $schema: ../../../json_schemas/test_story.schema.yaml description: Test get SQL Queries stats. chapters: - - synopsis: Get stats from PPL Query. + - synopsis: Get stats from PPL query (GET). + path: /_plugins/_ppl/stats + method: GET + response: + status: 200 + content_type: text/plain + - synopsis: Get stats from PPL query (POST). path: /_plugins/_ppl/stats method: POST response: diff --git a/tests/default/rollups/jobs.yaml b/tests/default/rollups/jobs.yaml new file mode 100644 index 000000000..29dd4ffbc --- /dev/null +++ b/tests/default/rollups/jobs.yaml @@ -0,0 +1,78 @@ + +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test index rollups. + +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: books, _id: book_1392214}} + - {author: Harper Lee, title: To Kill a Mockingbird, order_date: '2021-02-27T03:56:10+00:00', pages: 285} + - {create: {_index: books, _id: book_1392215}} + - {author: Elizabeth Rudnick, title: Beauty and the Beast, order_date: '2024-12-09T04:37:10-05:00', pages: 275} +epilogues: + - path: /_plugins/_rollup/jobs/books + method: DELETE + status: [200, 404] + - path: /books + method: DELETE + status: [200, 404] +chapters: + - synopsis: Create an index rollup job. + path: /_plugins/_rollup/jobs/{id} + method: PUT + parameters: + id: books + request: + payload: + rollup: + enabled: true + description: Rollup books. + schedule: + interval: + period: 1 + unit: Minutes + source_index: books + target_index: books_by_order_date + page_size: 1 + delay: 0 + continuous: false + dimensions: + - date_histogram: + source_field: order_date + fixed_interval: 1h + - terms: + source_field: title + metrics: + - source_field: pages + metrics: + - sum: {} + - avg: {} + response: + status: 201 + payload: + _id: books + rollup: + rollup_id: books + enabled: true + - synopsis: Get an index rollup job. + path: /_plugins/_rollup/jobs/{id} + method: GET + parameters: + id: books + response: + status: 200 + payload: + _id: books + rollup: + rollup_id: books + - synopsis: Delete an index rollup job. + path: /_plugins/_rollup/jobs/{id} + method: DELETE + parameters: + id: books diff --git a/tests/default/rollups/jobs/explain.yaml b/tests/default/rollups/jobs/explain.yaml new file mode 100644 index 000000000..a7659d5a4 --- /dev/null +++ b/tests/default/rollups/jobs/explain.yaml @@ -0,0 +1,59 @@ + +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test index rollups. + +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: books, _id: book_1392214}} + - {author: Harper Lee, title: To Kill a Mockingbird, order_date: '2021-02-27T03:56:10+00:00', pages: 285} + - {create: {_index: books, _id: book_1392215}} + - {author: Elizabeth Rudnick, title: Beauty and the Beast, order_date: '2024-12-09T04:37:10-05:00', pages: 275} + - path: /_plugins/_rollup/jobs/books_rollup_job + method: PUT + request: + payload: + rollup: + enabled: true + description: Rollup books. + schedule: + interval: + period: 1 + unit: Minutes + source_index: books + target_index: books_by_order_date + page_size: 1 + delay: 0 + continuous: false + dimensions: + - date_histogram: + source_field: order_date + fixed_interval: 1h + - terms: + source_field: title + metrics: + - source_field: pages + metrics: + - sum: {} + - avg: {} + - path: /_plugins/_rollup/jobs/books_rollup_job/_start + method: POST +epilogues: + - path: /_plugins/_rollup/jobs/books_rollup_job + method: DELETE + status: [200, 404] + - path: /books + method: DELETE + status: [200, 404] +chapters: + - synopsis: Explain an index rollup job. + path: /_plugins/_rollup/jobs/{id}/_explain + method: GET + parameters: + id: books_rollup_job diff --git a/tests/default/rollups/jobs/start.yaml b/tests/default/rollups/jobs/start.yaml new file mode 100644 index 000000000..16ee3ba95 --- /dev/null +++ b/tests/default/rollups/jobs/start.yaml @@ -0,0 +1,57 @@ + +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test index rollups. + +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: books, _id: book_1392214}} + - {author: Harper Lee, title: To Kill a Mockingbird, order_date: '2021-02-27T03:56:10+00:00', pages: 285} + - {create: {_index: books, _id: book_1392215}} + - {author: Elizabeth Rudnick, title: Beauty and the Beast, order_date: '2024-12-09T04:37:10-05:00', pages: 275} + - path: /_plugins/_rollup/jobs/books + method: PUT + request: + payload: + rollup: + enabled: true + description: Rollup books. + schedule: + interval: + period: 1 + unit: Minutes + source_index: books + target_index: books_by_order_date + page_size: 1 + delay: 0 + continuous: false + dimensions: + - date_histogram: + source_field: order_date + fixed_interval: 1h + - terms: + source_field: title + metrics: + - source_field: pages + metrics: + - sum: {} + - avg: {} +epilogues: + - path: /_plugins/_rollup/jobs/books + method: DELETE + status: [200, 404] + - path: /books + method: DELETE + status: [200, 404] +chapters: + - synopsis: Start an index rollup job. + path: /_plugins/_rollup/jobs/{id}/_start + method: POST + parameters: + id: books diff --git a/tests/default/rollups/jobs/stop.yaml b/tests/default/rollups/jobs/stop.yaml new file mode 100644 index 000000000..7cef61e68 --- /dev/null +++ b/tests/default/rollups/jobs/stop.yaml @@ -0,0 +1,59 @@ + +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test index rollups. + +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: books, _id: book_1392214}} + - {author: Harper Lee, title: To Kill a Mockingbird, order_date: '2021-02-27T03:56:10+00:00', pages: 285} + - {create: {_index: books, _id: book_1392215}} + - {author: Elizabeth Rudnick, title: Beauty and the Beast, order_date: '2024-12-09T04:37:10-05:00', pages: 275} + - path: /_plugins/_rollup/jobs/books + method: PUT + request: + payload: + rollup: + enabled: true + description: Rollup books. + schedule: + interval: + period: 1 + unit: Minutes + source_index: books + target_index: books_by_order_date + page_size: 1 + delay: 0 + continuous: false + dimensions: + - date_histogram: + source_field: order_date + fixed_interval: 1h + - terms: + source_field: title + metrics: + - source_field: pages + metrics: + - sum: {} + - avg: {} + - path: /_plugins/_rollup/jobs/books/_start + method: POST +epilogues: + - path: /_plugins/_rollup/jobs/books + method: DELETE + status: [200, 404] + - path: /books + method: DELETE + status: [200, 404] +chapters: + - synopsis: Stop an index rollup job. + path: /_plugins/_rollup/jobs/{id}/_stop + method: POST + parameters: + id: books diff --git a/tests/default/search/template.yaml b/tests/default/search/template.yaml index 956f6fbd9..a0aeff1a1 100644 --- a/tests/default/search/template.yaml +++ b/tests/default/search/template.yaml @@ -5,6 +5,9 @@ epilogues: - path: /movies method: DELETE status: [200, 404] + - path: /movies + method: DELETE + status: [200, 404] prologues: - path: /_bulk method: POST diff --git a/tests/default/sql/stats.yaml b/tests/default/sql/stats.yaml index 87a181b44..9c0cb3ade 100644 --- a/tests/default/sql/stats.yaml +++ b/tests/default/sql/stats.yaml @@ -3,9 +3,15 @@ $schema: ../../../json_schemas/test_story.schema.yaml description: Test get SQL Queries stats. chapters: - - synopsis: Get stats from SQL Query. + - synopsis: Get stats from SQL Query (POST). path: /_plugins/_sql/stats method: POST + response: + status: 200 + content_type: text/plain + - synopsis: Get stats from SQL Query (GET). + path: /_plugins/_sql/stats + method: GET response: status: 200 content_type: text/plain \ No newline at end of file diff --git a/tests/default/transforms/transform.yaml b/tests/default/transforms/transform.yaml new file mode 100644 index 000000000..eaae0a839 --- /dev/null +++ b/tests/default/transforms/transform.yaml @@ -0,0 +1,93 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test transform jobs. +prologues: + - path: /{index}/_doc + method: POST + parameters: + index: movies + refresh: true + request: + payload: + title: Beauty and the Beast + year: 91 + status: [201] +epilogues: + - path: /_plugins/_transform/movies-to-films + method: DELETE + status: [200, 404] + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Create a transform job. + id: transform + path: /_plugins/_transform/{id} + method: PUT + parameters: + id: movies-to-films + request: + payload: + transform: + enabled: false + description: A transform. + source_index: movies + target_index: films + data_selection_query: + match_all: {} + page_size: 10 + groups: + - terms: + source_field: year + target_field: produced + schedule: + interval: + period: 1 + unit: Minutes + start_time: 1602100553 + response: + status: 201 + output: + primary_term: payload._primary_term + seq_no: payload._seq_no + - synopsis: Update a transform job. + path: /_plugins/_transform/{id} + method: PUT + parameters: + id: movies-to-films + if_primary_term: ${transform.primary_term} + if_seq_no: ${transform.seq_no} + request: + payload: + transform: + enabled: false + description: A transform. + source_index: movies + target_index: films + data_selection_query: + match_all: {} + page_size: 10 + groups: + - terms: + source_field: year + target_field: produced + schedule: + interval: + period: 1 + unit: Minutes + start_time: 1602100553 + response: + status: 200 + - synopsis: Get transform jobs. + path: /_plugins/_transform + method: GET + - synopsis: Get a transform job. + path: /_plugins/_transform/{id} + method: GET + parameters: + id: movies-to-films + - synopsis: Delete a transform job. + path: /_plugins/_transform/{id} + method: DELETE + parameters: + id: movies-to-films diff --git a/tests/default/transforms/transform/explain.yaml b/tests/default/transforms/transform/explain.yaml new file mode 100644 index 000000000..7adc70c4a --- /dev/null +++ b/tests/default/transforms/transform/explain.yaml @@ -0,0 +1,51 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test explaining a transform job. +prologues: + - path: /{index}/_doc + method: POST + parameters: + index: movies + refresh: true + request: + payload: + title: Beauty and the Beast + year: 91 + status: [201] + - path: /_plugins/_transform/movies-to-films + method: PUT + request: + payload: + transform: + enabled: false + description: A transform. + source_index: movies + target_index: films + data_selection_query: + match_all: {} + page_size: 10 + groups: + - terms: + source_field: year + target_field: produced + schedule: + interval: + period: 1 + unit: Minutes + start_time: 1602100553 + status: [201] +epilogues: + - path: /_plugins/_transform/movies-to-films + parameters: + force: true + method: DELETE + status: [200, 404] + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Explain a transform job. + path: /_plugins/_transform/{id}/_explain + method: GET + parameters: + id: movies-to-films diff --git a/tests/default/transforms/transform/start.yaml b/tests/default/transforms/transform/start.yaml new file mode 100644 index 000000000..55d681bcf --- /dev/null +++ b/tests/default/transforms/transform/start.yaml @@ -0,0 +1,51 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test starting a transform job. +prologues: + - path: /{index}/_doc + method: POST + parameters: + index: movies + refresh: true + request: + payload: + title: Beauty and the Beast + year: 91 + status: [201] + - path: /_plugins/_transform/movies-to-films + method: PUT + request: + payload: + transform: + enabled: false + description: A transform. + source_index: movies + target_index: films + data_selection_query: + match_all: {} + page_size: 10 + groups: + - terms: + source_field: year + target_field: produced + schedule: + interval: + period: 1 + unit: Minutes + start_time: 1602100553 + status: [201] +epilogues: + - path: /_plugins/_transform/movies-to-films + parameters: + force: true + method: DELETE + status: [200, 404] + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Start a transform job. + path: /_plugins/_transform/{id}/_start + method: POST + parameters: + id: movies-to-films diff --git a/tests/default/transforms/transform/stop.yaml b/tests/default/transforms/transform/stop.yaml new file mode 100644 index 000000000..7f3f9276e --- /dev/null +++ b/tests/default/transforms/transform/stop.yaml @@ -0,0 +1,53 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: Test stopping a transform job. +prologues: + - path: /{index}/_doc + method: POST + parameters: + index: movies + refresh: true + request: + payload: + title: Beauty and the Beast + year: 91 + status: [201] + - path: /_plugins/_transform/movies-to-films + method: PUT + request: + payload: + transform: + enabled: false + description: A transform. + source_index: movies + target_index: films + data_selection_query: + match_all: {} + page_size: 10 + groups: + - terms: + source_field: year + target_field: produced + schedule: + interval: + period: 1 + unit: Minutes + start_time: 1602100553 + status: [201] + - path: /_plugins/_transform/movies-to-films/_start + method: POST +epilogues: + - path: /_plugins/_transform/movies-to-films + parameters: + force: true + method: DELETE + status: [200, 404] + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Stop a transform job. + path: /_plugins/_transform/{id}/_stop + method: POST + parameters: + id: movies-to-films diff --git a/tests/plugins/notifications/notifications/feature/test.yaml b/tests/plugins/notifications/notifications/feature/test.yaml index e889155e1..084aa8b78 100644 --- a/tests/plugins/notifications/notifications/feature/test.yaml +++ b/tests/plugins/notifications/notifications/feature/test.yaml @@ -17,7 +17,7 @@ prologues: url: http://webhook:8080/ status: [200] chapters: - - synopsis: Test sending a notification. + - synopsis: Test sending a notification (GET). path: /_plugins/_notifications/feature/test/{config_id} method: GET parameters: @@ -31,6 +31,20 @@ chapters: delivery_status: status_code: '200' status_text: '{"ok":"true"}' + - synopsis: Test sending a notification (POST). + path: /_plugins/_notifications/feature/test/{config_id} + method: POST + parameters: + config_id: custom-webhook-configuration + response: + status: 200 + payload: + status_list: + - config_id: custom-webhook-configuration + config_type: webhook + delivery_status: + status_code: '200' + status_text: '{"ok":"true"}' epilogues: - path: /_plugins/_notifications/configs/custom-webhook-configuration method: DELETE diff --git a/tests/plugins/replication/autofollow.yaml b/tests/plugins/replication/autofollow.yaml index 7f3cb8352..f13359011 100644 --- a/tests/plugins/replication/autofollow.yaml +++ b/tests/plugins/replication/autofollow.yaml @@ -1,6 +1,7 @@ $schema: ../../../json_schemas/test_story.schema.yaml -description: Test replication APIs specs. +description: Test replication APIs. +version: '>= 1.1' prologues: - path: /{index} method: PUT @@ -10,6 +11,8 @@ prologues: payload: {} - path: /_bulk method: POST + parameters: + refresh: true request: content_type: application/x-ndjson payload: @@ -17,10 +20,6 @@ prologues: - {director: Bennett Miller, title: Moneyball, year: 2011} - {create: {_index: movies, _id: movie_1392215}} - {director: Badge Dale, title: The Secret Soldiers, year: 2016} - - path: /{index}/_refresh - method: POST - parameters: - index: movies - path: /_cluster/settings method: PUT request: @@ -31,7 +30,6 @@ prologues: leader-cluster: seeds: ['172.22.0.3:9300'] - path: /_plugins/_replication/{index}/_start - version: '>= 1.1' method: PUT parameters: index: movies-names @@ -58,7 +56,6 @@ epilogues: chapters: - synopsis: Create replication rule. path: /_plugins/_replication/_autofollow - version: '>= 1.1' method: POST request: payload: @@ -70,15 +67,8 @@ chapters: follower_cluster_role: admin response: status: 200 - - synopsis: Get auto-follow stats. - path: /_plugins/_replication/autofollow_stats - version: '>= 1.1' - method: GET - response: - status: 200 - synopsis: Delete replication rule. path: /_plugins/_replication/_autofollow - version: '>= 1.1' method: DELETE request: payload: diff --git a/tests/plugins/replication/autofollow_stats.yaml b/tests/plugins/replication/autofollow_stats.yaml new file mode 100644 index 000000000..c586bf9d9 --- /dev/null +++ b/tests/plugins/replication/autofollow_stats.yaml @@ -0,0 +1,78 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test replication APIs specs. +version: '>= 1.1' +prologues: + - path: /{index} + method: PUT + parameters: + index: movies + request: + payload: {} + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: movies, _id: movie_1392214}} + - {director: Bennett Miller, title: Moneyball, year: 2011} + - {create: {_index: movies, _id: movie_1392215}} + - {director: Badge Dale, title: The Secret Soldiers, year: 2016} + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + cluster: + remote: + leader-cluster: + seeds: ['172.22.0.3:9300'] + - path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: movies-names + request: + payload: + leader_alias: leader-cluster + leader_index: movies + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access + - path: /_plugins/_replication/_autofollow + method: POST + request: + payload: + leader_alias: leader-cluster + name: movies + pattern: 'movies-*' + use_roles: + leader_cluster_role: leader_role + follower_cluster_role: admin +epilogues: + - path: /_plugins/_replication/_autofollow + method: DELETE + request: + payload: + leader_alias: leader-cluster + name: movies + status: [200, 404] + - path: /_plugins/_replication/{index}/_stop + method: POST + parameters: + index: movies-names + request: + payload: {} + - path: /movies-names + method: DELETE + status: [200, 404] + - path: /movies + method: DELETE + status: [200, 404] +chapters: + - synopsis: Get auto-follow stats. + path: /_plugins/_replication/autofollow_stats + method: GET + response: + status: 200 diff --git a/tests/plugins/replication/pause.yaml b/tests/plugins/replication/pause.yaml new file mode 100644 index 000000000..217efceae --- /dev/null +++ b/tests/plugins/replication/pause.yaml @@ -0,0 +1,63 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test pausing a replication. +version: '>= 1.1' +warnings: + multiple-paths-detected: false +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: music, _id: music_1392214}} + - {author: Selena Gomez, title: Love you like a love song, year: 2011} + - {create: {_index: music, _id: music_1392215}} + - {author: Justin Bieber, title: Baby, year: 2010} + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + cluster: + remote: + leader-cluster: + seeds: ['172.22.0.3:9300'] + - path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: music-names + request: + payload: + leader_alias: leader-cluster + leader_index: music + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access +epilogues: + - path: /_plugins/_replication/{index}/_stop + method: POST + parameters: + index: music-names + request: + payload: {} + - path: /music-names + method: DELETE + status: [200, 404] + - path: /music + method: DELETE + status: [200, 404] +chapters: + - synopsis: Pause replication. + path: /_plugins/_replication/{index}/_pause + method: POST + retry: + count: 3 + parameters: + index: music-names + request: + payload: {} + response: + status: 200 diff --git a/tests/plugins/replication/resume.yaml b/tests/plugins/replication/resume.yaml new file mode 100644 index 000000000..275e9fcd9 --- /dev/null +++ b/tests/plugins/replication/resume.yaml @@ -0,0 +1,69 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test pausing a replication. +version: '>= 1.1' +warnings: + multiple-paths-detected: false +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: music, _id: music_1392214}} + - {author: Selena Gomez, title: Love you like a love song, year: 2011} + - {create: {_index: music, _id: music_1392215}} + - {author: Justin Bieber, title: Baby, year: 2010} + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + cluster: + remote: + leader-cluster: + seeds: ['172.22.0.3:9300'] + - path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: music-names + request: + payload: + leader_alias: leader-cluster + leader_index: music + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access + - path: /_plugins/_replication/music-names/_pause + method: POST + retry: + count: 3 + request: + payload: {} +epilogues: + - path: /_plugins/_replication/{index}/_stop + method: POST + parameters: + index: music-names + request: + payload: {} + - path: /music-names + method: DELETE + status: [200, 404] + - path: /music + method: DELETE + status: [200, 404] +chapters: + - synopsis: Resume replication. + path: /_plugins/_replication/{index}/_resume + method: POST + retry: + count: 3 + parameters: + index: music-names + request: + payload: {} + response: + status: 200 diff --git a/tests/plugins/replication/start.yaml b/tests/plugins/replication/start.yaml new file mode 100644 index 000000000..ccb4eb667 --- /dev/null +++ b/tests/plugins/replication/start.yaml @@ -0,0 +1,53 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test starting replication. +version: '>= 1.1' +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: books, _id: book_1392214}} + - {author: Harper Lee, title: To Kill a Mockingbird, year: 1960} + - {create: {_index: books, _id: book_1392215}} + - {author: Elizabeth Rudnick, title: Beauty and the Beast, year: 1991} + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + cluster: + remote: + leader-cluster: + seeds: ['172.22.0.3:9300'] +epilogues: + - path: /_plugins/_replication/{index}/_stop + method: POST + parameters: + index: books-names + request: + payload: {} + - path: /books-names + method: DELETE + status: [200, 404] + - path: /books + method: DELETE + status: [200, 404] +chapters: + - synopsis: Start replication. + path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: books-names + request: + payload: + leader_alias: leader-cluster + leader_index: books + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access + response: + status: 200 diff --git a/tests/plugins/replication/stats.yaml b/tests/plugins/replication/stats.yaml index c4e8102af..57f85baee 100644 --- a/tests/plugins/replication/stats.yaml +++ b/tests/plugins/replication/stats.yaml @@ -1,15 +1,14 @@ $schema: ../../../json_schemas/test_story.schema.yaml -description: Test replication APIs specs. +description: Test replication stats. +version: '>= 1.1' +warnings: + multiple-paths-detected: false prologues: - - path: /{index} - method: PUT - parameters: - index: music - request: - payload: {} - path: /_bulk method: POST + parameters: + refresh: true request: content_type: application/x-ndjson payload: @@ -17,10 +16,6 @@ prologues: - {author: Selena Gomez, title: Love you like a love song, year: 2011} - {create: {_index: music, _id: music_1392215}} - {author: Justin Bieber, title: Baby, year: 2010} - - path: /{index}/_refresh - method: POST - parameters: - index: music - path: /_cluster/settings method: PUT request: @@ -31,7 +26,6 @@ prologues: leader-cluster: seeds: ['172.22.0.3:9300'] - path: /_plugins/_replication/{index}/_start - version: '>= 1.1' method: PUT parameters: index: music-names @@ -56,29 +50,18 @@ epilogues: method: DELETE status: [200, 404] chapters: - - synopsis: Get replication stats. - path: /_plugins/_replication/{index}/_status - version: '>= 1.1' - method: GET - parameters: - index: music-names - response: - status: 200 - synopsis: Get leader stats. path: /_plugins/_replication/leader_stats - version: '>= 1.1' method: GET response: status: 200 - synopsis: Get follower stats. path: /_plugins/_replication/follower_stats - version: '>= 1.1' method: GET response: status: 200 - synopsis: Get auto-follow stats. path: /_plugins/_replication/autofollow_stats - version: '>= 1.1' method: GET response: status: 200 \ No newline at end of file diff --git a/tests/plugins/replication/status.yaml b/tests/plugins/replication/status.yaml new file mode 100644 index 000000000..402883631 --- /dev/null +++ b/tests/plugins/replication/status.yaml @@ -0,0 +1,59 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test replication status. +version: '>= 1.1' +warnings: + multiple-paths-detected: false +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: music, _id: music_1392214}} + - {author: Selena Gomez, title: Love you like a love song, year: 2011} + - {create: {_index: music, _id: music_1392215}} + - {author: Justin Bieber, title: Baby, year: 2010} + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + cluster: + remote: + leader-cluster: + seeds: ['172.22.0.3:9300'] + - path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: music-names + request: + payload: + leader_alias: leader-cluster + leader_index: music + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access +epilogues: + - path: /_plugins/_replication/{index}/_stop + method: POST + parameters: + index: music-names + request: + payload: {} + - path: /music-names + method: DELETE + status: [200, 404] + - path: /music + method: DELETE + status: [200, 404] +chapters: + - synopsis: Get replication status. + path: /_plugins/_replication/{index}/_status + method: GET + parameters: + index: music-names + response: + status: 200 diff --git a/tests/plugins/replication/stop.yaml b/tests/plugins/replication/stop.yaml new file mode 100644 index 000000000..6d50c8fc0 --- /dev/null +++ b/tests/plugins/replication/stop.yaml @@ -0,0 +1,52 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test stopping a replication. +version: '>= 1.1' +warnings: + multiple-paths-detected: false +prologues: + - path: /_bulk + method: POST + parameters: + refresh: true + request: + content_type: application/x-ndjson + payload: + - {create: {_index: music, _id: music_1392214}} + - {author: Selena Gomez, title: Love you like a love song, year: 2011} + - {create: {_index: music, _id: music_1392215}} + - {author: Justin Bieber, title: Baby, year: 2010} + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + cluster: + remote: + leader-cluster: + seeds: ['172.22.0.3:9300'] + - path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: music-names + request: + payload: + leader_alias: leader-cluster + leader_index: music + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access +epilogues: + - path: /music + method: DELETE + status: [200, 404] +chapters: + - synopsis: Stop replication. + path: /_plugins/_replication/{index}/_stop + method: POST + parameters: + index: music-names + request: + payload: {} + response: + status: 200 diff --git a/tests/plugins/replication/index.yaml b/tests/plugins/replication/update.yaml similarity index 78% rename from tests/plugins/replication/index.yaml rename to tests/plugins/replication/update.yaml index a70a0b991..b3eae554c 100644 --- a/tests/plugins/replication/index.yaml +++ b/tests/plugins/replication/update.yaml @@ -1,15 +1,12 @@ $schema: ../../../json_schemas/test_story.schema.yaml -description: Test replication APIs specs. +description: Test updating settings for a started replication. +version: '>= 1.1' prologues: - - path: /{index} - method: PUT - parameters: - index: books - request: - payload: {} - path: /_bulk method: POST + parameters: + refresh: true request: content_type: application/x-ndjson payload: @@ -17,10 +14,6 @@ prologues: - {author: Harper Lee, title: To Kill a Mockingbird, year: 1960} - {create: {_index: books, _id: book_1392215}} - {author: Elizabeth Rudnick, title: Beauty and the Beast, year: 1991} - - path: /{index}/_refresh - method: POST - parameters: - index: books - path: /_cluster/settings method: PUT request: @@ -30,6 +23,17 @@ prologues: remote: leader-cluster: seeds: ['172.22.0.3:9300'] + - path: /_plugins/_replication/{index}/_start + method: PUT + parameters: + index: books-names + request: + payload: + leader_alias: leader-cluster + leader_index: books + use_roles: + leader_cluster_role: all_access + follower_cluster_role: all_access epilogues: - path: /_plugins/_replication/{index}/_stop method: POST @@ -44,24 +48,8 @@ epilogues: method: DELETE status: [200, 404] chapters: - - synopsis: Start replication. - path: /_plugins/_replication/{index}/_start - version: '>= 1.1' - method: PUT - parameters: - index: books-names - request: - payload: - leader_alias: leader-cluster - leader_index: books - use_roles: - leader_cluster_role: all_access - follower_cluster_role: all_access - response: - status: 200 - - synopsis: Update settings. + - synopsis: Update replication settings. path: /_plugins/_replication/{index}/_update - version: '>= 1.1' method: PUT parameters: index: books-names diff --git a/tests/snapshot/snapshot/cleanup.yaml b/tests/snapshot/snapshot/cleanup.yaml new file mode 100644 index 000000000..09fd08ebf --- /dev/null +++ b/tests/snapshot/snapshot/cleanup.yaml @@ -0,0 +1,27 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test cleaning up a snapshot repository. +prologues: + - path: /_snapshot/my-fs-repository + method: POST + request: + payload: + type: fs + settings: + location: /tmp/opensearch/repo +epilogues: + - path: /_snapshot/my-fs-repository + method: DELETE + status: [200, 404] +chapters: + - synopsis: Verify snapshot repository. + path: /_snapshot/{repository}/_cleanup + method: POST + parameters: + repository: my-fs-repository + response: + status: 200 + payload: + results: + deleted_bytes: 0 + deleted_blobs: 0 diff --git a/tests/snapshot/snapshot/clone.yaml b/tests/snapshot/snapshot/clone.yaml new file mode 100644 index 000000000..8e6491ca3 --- /dev/null +++ b/tests/snapshot/snapshot/clone.yaml @@ -0,0 +1,40 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test cloning a snapshot. +prologues: + - path: /_snapshot/my-fs-repository + method: POST + request: + payload: + type: fs + settings: + location: /tmp/opensearch/repo + - path: /_snapshot/my-fs-repository/my-test-snapshot + method: PUT + parameters: + wait_for_completion: true + request: + payload: + indices: '*' + partial: true +epilogues: + - path: /_snapshot/my-fs-repository/my-test-snapshot + method: DELETE + status: [200, 404] + - path: /_snapshot/my-fs-repository/my-cloned-snapshot + method: DELETE + status: [200, 404] + - path: /_snapshot/my-fs-repository + method: DELETE + status: [200, 404] +chapters: + - synopsis: Clone a snapshot. + path: /_snapshot/{repository}/{snapshot}/_clone/{target_snapshot} + method: PUT + parameters: + repository: my-fs-repository + snapshot: my-test-snapshot + target_snapshot: my-cloned-snapshot + request: + payload: + indices: '*' diff --git a/tests/snapshot/snapshot/snapshot.yaml b/tests/snapshot/snapshot/snapshot.yaml index ca77a39bb..2ce557f4b 100644 --- a/tests/snapshot/snapshot/snapshot.yaml +++ b/tests/snapshot/snapshot/snapshot.yaml @@ -13,9 +13,12 @@ epilogues: status: [200, 404] parameters: repository: my-fs-repository -prologues: - - path: /_snapshot/{repository} - method: PUT +chapters: + - synopsis: Create and update a snapshot repository. + path: /_snapshot/{repository} + method: + - POST + - PUT parameters: repository: my-fs-repository request: @@ -23,7 +26,16 @@ prologues: type: fs settings: location: /tmp/opensearch/repo -chapters: + - synopsis: Get all snapshot repositories. + path: /_snapshot + method: GET + response: + status: 200 + payload: + my-fs-repository: + type: fs + settings: + location: /tmp/opensearch/repo - synopsis: Get information about a repository. path: /_snapshot/{repository} method: GET @@ -77,3 +89,14 @@ chapters: payload: snapshots: - snapshot: my-test-snapshot + - synopsis: Delete a snapshot. + path: /_snapshot/{repository}/{snapshot} + method: DELETE + parameters: + repository: my-fs-repository + snapshot: my-test-snapshot + - synopsis: Delete a snapshot repository. + path: /_snapshot/{repository} + method: DELETE + parameters: + repository: my-fs-repository diff --git a/tests/snapshot/snapshot/status.yaml b/tests/snapshot/snapshot/status.yaml new file mode 100644 index 000000000..af00860da --- /dev/null +++ b/tests/snapshot/snapshot/status.yaml @@ -0,0 +1,66 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test _snapshot/{repository}/snapshot endpoints. +epilogues: + - path: /_snapshot/{repository}/{snapshot} + method: DELETE + status: [200, 404] + parameters: + repository: my-fs-repository + snapshot: my-test-snapshot + - path: /_snapshot/{repository} + method: DELETE + status: [200, 404] + parameters: + repository: my-fs-repository +prologues: + - path: /_snapshot/{repository} + method: PUT + parameters: + repository: my-fs-repository + request: + payload: + type: fs + settings: + location: /tmp/opensearch/repo + - path: /_snapshot/{repository}/{snapshot} + method: PUT + parameters: + repository: my-fs-repository + snapshot: my-test-snapshot + request: + payload: + indices: '*' + ignore_unavailable: true + include_global_state: false + partial: true +chapters: + - synopsis: Get status of all running snapshots. + path: /_snapshot/_status + method: GET + response: + status: 200 + payload: + snapshots: + - snapshot: my-test-snapshot + - synopsis: Get status of all running snapshots in specific repository. + path: /_snapshot/{repository}/_status + method: GET + parameters: + repository: my-fs-repository + response: + status: 200 + payload: + snapshots: + - snapshot: my-test-snapshot + - synopsis: Get status of specific snapshot in specific repository. + path: /_snapshot/{repository}/{snapshot}/_status + method: GET + parameters: + repository: my-fs-repository + snapshot: my-test-snapshot + response: + status: 200 + payload: + snapshots: + - snapshot: my-test-snapshot \ No newline at end of file diff --git a/tests/snapshot/snapshot/verify.yaml b/tests/snapshot/snapshot/verify.yaml new file mode 100644 index 000000000..fa2347f42 --- /dev/null +++ b/tests/snapshot/snapshot/verify.yaml @@ -0,0 +1,25 @@ +$schema: ../../../json_schemas/test_story.schema.yaml + +description: Test verifying a snapshot repository. +prologues: + - path: /_snapshot/my-fs-repository + method: POST + request: + payload: + type: fs + settings: + location: /tmp/opensearch/repo +epilogues: + - path: /_snapshot/my-fs-repository + method: DELETE + status: [200, 404] +chapters: + - synopsis: Verify snapshot repository. + path: /_snapshot/{repository}/_verify + method: POST + parameters: + repository: my-fs-repository + response: + status: 200 + payload: + nodes: {} \ No newline at end of file diff --git a/tools/src/tester/ChapterEvaluator.ts b/tools/src/tester/ChapterEvaluator.ts index 04df8af42..73ce1045e 100644 --- a/tools/src/tester/ChapterEvaluator.ts +++ b/tools/src/tester/ChapterEvaluator.ts @@ -7,7 +7,7 @@ * compatible open source license. */ -import { type Chapter, type ActualResponse, type Payload } from './types/story.types' +import { type ActualResponse, type Payload } from './types/story.types' import { type ChapterEvaluation, type Evaluation, Result, EvaluationWithOutput } from './types/eval.types' import { type ParsedOperation } from './types/spec.types' import { overall_result } from './helpers' @@ -21,6 +21,7 @@ import _ from 'lodash' import { Logger } from 'Logger' import { sleep, to_json } from '../helpers' import { APPLICATION_JSON } from "./MimeTypes"; +import { ParsedChapter } from './types/parsed_story.types' export default class ChapterEvaluator { private readonly logger: Logger @@ -35,7 +36,7 @@ export default class ChapterEvaluator { this.logger = logger } - async evaluate(chapter: Chapter, skip: boolean, story_outputs: StoryOutputs): Promise { + async evaluate(chapter: ParsedChapter, skip: boolean, story_outputs: StoryOutputs): Promise { if (skip) return { title: chapter.synopsis, overall: { result: Result.SKIPPED } } const operation = this._operation_locator.locate_operation(chapter) @@ -61,7 +62,7 @@ export default class ChapterEvaluator { return result } - async #evaluate(chapter: Chapter, operation: ParsedOperation, story_outputs: StoryOutputs, retries?: number): Promise { + async #evaluate(chapter: ParsedChapter, operation: ParsedOperation, story_outputs: StoryOutputs, retries?: number): Promise { const response = await this._chapter_reader.read(chapter, story_outputs) const params = this.#evaluate_parameters(chapter, operation, story_outputs) const request = this.#evaluate_request(chapter, operation, story_outputs) @@ -110,7 +111,7 @@ export default class ChapterEvaluator { return result } - #evaluate_parameters(chapter: Chapter, operation: ParsedOperation, story_outputs: StoryOutputs): Record { + #evaluate_parameters(chapter: ParsedChapter, operation: ParsedOperation, story_outputs: StoryOutputs): Record { const parameters: Record = story_outputs.resolve_value(chapter.parameters) ?? {} return Object.fromEntries(Object.entries(parameters).map(([name, parameter]) => { const schema = operation.parameters[name]?.schema @@ -120,7 +121,7 @@ export default class ChapterEvaluator { })) } - #evaluate_request(chapter: Chapter, operation: ParsedOperation, story_outputs: StoryOutputs): Evaluation { + #evaluate_request(chapter: ParsedChapter, operation: ParsedOperation, story_outputs: StoryOutputs): Evaluation { if (chapter.request?.payload === undefined) return { result: Result.PASSED } const content_type = chapter.request.content_type ?? APPLICATION_JSON const schema = operation.requestBody?.content[content_type]?.schema @@ -129,7 +130,7 @@ export default class ChapterEvaluator { return this._schema_validator.validate(schema, payload) } - #evaluate_status(chapter: Chapter, response: ActualResponse): Evaluation { + #evaluate_status(chapter: ParsedChapter, response: ActualResponse): Evaluation { const expected_status = chapter.response?.status ?? 200 if (response.status === expected_status && response.error === undefined) return { result: Result.PASSED } @@ -166,7 +167,7 @@ export default class ChapterEvaluator { return messages.length > 0 ? { result: Result.FAILED, message: _.join(messages, ', ') } : { result: Result.PASSED } } - #evaluate_payload_schema(chapter: Chapter, response: ActualResponse, operation: ParsedOperation): Evaluation { + #evaluate_payload_schema(chapter: ParsedChapter, response: ActualResponse, operation: ParsedOperation): Evaluation { const content_type = chapter.response?.content_type ?? APPLICATION_JSON if (response.content_type !== content_type) { diff --git a/tools/src/tester/ChapterReader.ts b/tools/src/tester/ChapterReader.ts index ad34d715b..c43698bb7 100644 --- a/tools/src/tester/ChapterReader.ts +++ b/tools/src/tester/ChapterReader.ts @@ -7,7 +7,7 @@ * compatible open source license. */ -import { type ChapterRequest, type ActualResponse, type Parameter } from './types/story.types' +import { ChapterRequest, type ActualResponse, type Parameter } from './types/story.types' import { type OpenSearchHttpClient } from '../OpenSearchHttpClient' import { type StoryOutputs } from './StoryOutputs' import { Logger } from 'Logger' @@ -62,7 +62,7 @@ export default class ChapterReader { response.status = e.response.status response.content_type = e.response.headers['content-type']?.split(';')[0] response.payload = this.#deserialize_payload(e.response.data, response.content_type) - response.message = response.payload.error?.reason ?? e.response.statusText + response.message = response.payload?.error?.reason ?? e.response.statusText this.logger.info(`<= ${response.status} (${response.content_type}) | ${response.payload !== undefined ? to_json(response.payload) : response.message}`) } }) diff --git a/tools/src/tester/OperationLocator.ts b/tools/src/tester/OperationLocator.ts index f5918f2bb..bc256a869 100644 --- a/tools/src/tester/OperationLocator.ts +++ b/tools/src/tester/OperationLocator.ts @@ -9,9 +9,9 @@ import { type OpenAPIV3 } from 'openapi-types' import { resolve_ref } from '../helpers' -import { type Chapter } from './types/story.types' import { type ParsedOperation } from './types/spec.types' import _ from 'lodash' +import { ParsedChapter } from './types/parsed_story.types' export default class OperationLocator { private readonly spec: OpenAPIV3.Document @@ -21,7 +21,7 @@ export default class OperationLocator { this.spec = spec } - locate_operation (chapter: Chapter): ParsedOperation | undefined { + locate_operation (chapter: ParsedChapter): ParsedOperation | undefined { const path = chapter.path const method = chapter.method.toLowerCase() as OpenAPIV3.HttpMethods const cache_key = path + method diff --git a/tools/src/tester/StoryEvaluator.ts b/tools/src/tester/StoryEvaluator.ts index e464f3f60..a17df942f 100644 --- a/tools/src/tester/StoryEvaluator.ts +++ b/tools/src/tester/StoryEvaluator.ts @@ -7,7 +7,7 @@ * compatible open source license. */ -import { ChapterRequest, Parameter, type Chapter, type Story, type SupplementalChapter } from './types/story.types' +import { ChapterRequest, Parameter, SupplementalChapter } from './types/story.types' import { type StoryFile, type ChapterEvaluation, Result, type StoryEvaluation, OutputReference } from './types/eval.types' import type ChapterEvaluator from './ChapterEvaluator' import { overall_result } from './helpers' @@ -16,6 +16,7 @@ import SupplementalChapterEvaluator from './SupplementalChapterEvaluator' import { ChapterOutput } from './ChapterOutput' import * as semver from '../_utils/semver' import _ from 'lodash' +import { ParsedChapter, ParsedStory } from './types/parsed_story.types' export default class StoryEvaluator { private readonly _chapter_evaluator: ChapterEvaluator @@ -85,14 +86,14 @@ export default class StoryEvaluator { return result } - #chapter_warnings(story: Story): string[] | undefined { + #chapter_warnings(story: ParsedStory): string[] | undefined { const result = _.compact([ this.#warning_if_mismatched_chapter_paths(story) ]) return result.length > 0 ? result : undefined } - #warning_if_mismatched_chapter_paths(story: Story): string | undefined { + #warning_if_mismatched_chapter_paths(story: ParsedStory): string | undefined { if (story.warnings?.['multiple-paths-detected'] === false) return const paths = _.compact(_.map(story.chapters, (chapter) => { if (chapter.warnings?.['multiple-paths-detected'] === false) return @@ -105,20 +106,17 @@ export default class StoryEvaluator { } } - async #evaluate_chapters(chapters: Chapter[], has_errors: boolean, dry_run: boolean, story_outputs: StoryOutputs, version?: string, distribution?: string): Promise { + async #evaluate_chapters(chapters: ParsedChapter[], has_errors: boolean, dry_run: boolean, story_outputs: StoryOutputs, version?: string, distribution?: string): Promise { const evaluations: ChapterEvaluation[] = [] for (const chapter of chapters) { + const title = chapter.synopsis || `${chapter.method} ${chapter.path}` if (dry_run) { - const title = chapter.synopsis || `${chapter.method} ${chapter.path}` evaluations.push({ title, overall: { result: Result.SKIPPED, message: 'Dry Run' } }) } else if (distribution != undefined && chapter.distributions?.included !== undefined && chapter.distributions?.included.length > 0 && !chapter.distributions.included.includes(distribution)) { - const title = chapter.synopsis || `${chapter.method} ${chapter.path}` evaluations.push({ title, overall: { result: Result.SKIPPED, message: `Skipped because distribution ${distribution} is not ${chapter.distributions.included.length > 1 ? 'one of ' : ''}${chapter.distributions.included.join(', ')}.` } }) } else if (distribution != undefined && chapter.distributions?.excluded !== undefined && chapter.distributions?.excluded.length > 0 && chapter.distributions.excluded.includes(distribution)) { - const title = chapter.synopsis || `${chapter.method} ${chapter.path}` evaluations.push({ title, overall: { result: Result.SKIPPED, message: `Skipped because distribution ${distribution} is ${chapter.distributions.excluded.length > 1 ? 'one of ' : ''}${chapter.distributions.excluded.join(', ')}.` } }) } else if (version != undefined && chapter.version !== undefined && !semver.satisfies(version, chapter.version)) { - const title = chapter.synopsis || `${chapter.method} ${chapter.path}` evaluations.push({ title, overall: { result: Result.SKIPPED, message: `Skipped because version ${version} does not satisfy ${chapter.version}.` } }) } else { const evaluation = await this._chapter_evaluator.evaluate(chapter, has_errors, story_outputs) @@ -140,8 +138,8 @@ export default class StoryEvaluator { if (dry_run) { evaluations.push({ title, overall: { result: Result.SKIPPED, message: 'Dry Run' } }) } else { - const { evaluation, evaluation_error } = await this._supplemental_chapter_evaluator.evaluate(chapter, story_outputs) - has_errors = has_errors || evaluation_error + const evaluation = await this._supplemental_chapter_evaluator.evaluate(chapter, story_outputs) + has_errors = has_errors || (evaluation.overall.result !== Result.PASSED && evaluation.overall.result !== Result.SKIPPED) if (evaluation.output !== undefined && chapter.id !== undefined) { story_outputs.set_chapter_output(chapter.id, evaluation.output) } @@ -152,7 +150,7 @@ export default class StoryEvaluator { } // TODO: Refactor and move this logic into StoryValidator - static check_story_variables(story: Story, display_path: string, full_path: string): StoryEvaluation | undefined { + static check_story_variables(story: ParsedStory, display_path: string, full_path: string): StoryEvaluation | undefined { const story_outputs = new StoryOutputs() const prologues = (story.prologues ?? []).map((prologue) => { return StoryEvaluator.#check_chapter_variables(prologue, story_outputs) diff --git a/tools/src/tester/StoryParser.ts b/tools/src/tester/StoryParser.ts new file mode 100644 index 000000000..21f507cd0 --- /dev/null +++ b/tools/src/tester/StoryParser.ts @@ -0,0 +1,41 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +import _ from "lodash"; +import { Chapter, Story } from "./types/story.types"; +import { ParsedChapter, ParsedStory } from "./types/parsed_story.types"; + +export default class StoryParser { + static parse(story: Story): ParsedStory { + return { + ...story, + chapters: this.#expand_chapters(story.chapters), + } + } + + static #chapter_methods(methods: string[] | string): string[] { + return [...(Array.isArray(methods) ? methods : [methods])] + } + + static #expand_chapters(chapters?: Chapter[]): ParsedChapter[] { + if (chapters === undefined) return [] + return _.flatMap(_.map(chapters, (chapter) => { + return _.map(this.#chapter_methods(chapter.method), (method) => { + let synopsis = chapter.synopsis && Array.isArray(chapter.method) ? + `${chapter.synopsis} [${method}]` : + chapter.synopsis + return { + ...chapter, + synopsis, + method + } + }) + })) as ParsedChapter[] + } +} \ No newline at end of file diff --git a/tools/src/tester/SupplementalChapterEvaluator.ts b/tools/src/tester/SupplementalChapterEvaluator.ts index faa6c1004..9abfe19e4 100644 --- a/tools/src/tester/SupplementalChapterEvaluator.ts +++ b/tools/src/tester/SupplementalChapterEvaluator.ts @@ -12,10 +12,10 @@ import { ChapterOutput } from "./ChapterOutput"; import ChapterReader from "./ChapterReader"; import { StoryOutputs } from "./StoryOutputs"; import { overall_result } from "./helpers"; -import { ChapterEvaluation, Evaluation, Result } from "./types/eval.types"; -import { SupplementalChapter } from "./types/story.types"; +import { ChapterEvaluation, EvaluationWithOutput, Result } from './types/eval.types'; import { Logger } from "../Logger"; -import { to_json } from "../helpers"; +import { sleep, to_json } from "../helpers"; +import { SupplementalChapter } from "./types/story.types"; export default class SupplementalChapterEvaluator { private readonly _chapter_reader: ChapterReader; @@ -26,63 +26,47 @@ export default class SupplementalChapterEvaluator { this.logger = logger } - async evaluate(chapter: SupplementalChapter, story_outputs: StoryOutputs): Promise<{ evaluation: ChapterEvaluation, evaluation_error: boolean }> { + async evaluate(chapter: SupplementalChapter, story_outputs: StoryOutputs): Promise { const title = `${chapter.method} ${chapter.path}` + + let tries = chapter.retry && chapter.retry?.count > 0 ? chapter.retry.count + 1 : 1 + let chapter_evaluation: EvaluationWithOutput + + do { + chapter_evaluation = await this.#evaluate(chapter, story_outputs) + if (chapter_evaluation.evaluation.result === Result.PASSED) break + if (--tries == 0) break + this.logger.info(`Failed, retrying, ${tries == 1 ? '1 retry left' : `${tries} retries left`} ...`) + await sleep(chapter.retry?.wait ?? 1000) + } while (tries > 0) + + var result: ChapterEvaluation = { title, overall: chapter_evaluation.evaluation } + if (chapter_evaluation.output) result.output = chapter_evaluation.output + return result + } + + async #evaluate(chapter: SupplementalChapter, story_outputs: StoryOutputs): Promise { const response = await this._chapter_reader.read(chapter, story_outputs) - const status = chapter.status ?? [200, 201] const output_values_evaluation = ChapterOutput.extract_output_values(response, chapter.output) if (output_values_evaluation.output) this.logger.info(`$ ${to_json(output_values_evaluation.output)}`) - let response_evaluation: ChapterEvaluation - const passed_evaluation = { title, overall: { result: Result.PASSED } } - if (status.includes(response.status)) { - response_evaluation = passed_evaluation - } else { - response_evaluation = { - title, - overall: { - result: Result.ERROR, - message: response.message, - error: response.error as Error - } - } - } - if (output_values_evaluation.output) { - response_evaluation.output = output_values_evaluation.output - } + const status = chapter.status ?? [200, 201] + const overall = status.includes(response.status) ? { result: Result.PASSED } : { result: Result.ERROR, message: response.message, error: response.error as Error } + const result: Result = overall_result(_.compact([overall, output_values_evaluation.evaluation])) - const result = overall_result(_.compact([ - response_evaluation.overall, - output_values_evaluation.evaluation - ])) + var evaluation_result: EvaluationWithOutput = { evaluation: { result } } + if (output_values_evaluation.output) { evaluation_result.output = output_values_evaluation.output } - if (result === Result.PASSED) { - return { evaluation: passed_evaluation, evaluation_error: false } - } else { + if (result !== Result.PASSED) { const message_segments = [] - - if (response_evaluation.overall.result === Result.ERROR) { - message_segments.push(`${response_evaluation.overall.message}`) - } - - if (output_values_evaluation.evaluation.message !== undefined && output_values_evaluation.evaluation.result === Result.ERROR) { + if (response.message !== undefined) { message_segments.push(`${response.message}`) } + if (output_values_evaluation.evaluation.result === Result.ERROR) { message_segments.push(`${output_values_evaluation.evaluation.message}`) } - - const message = message_segments.join('\n') - - var overall: Evaluation = { result: Result.ERROR, message } - if (response.error !== undefined) overall.error = response.error as Error - - var evaluation: ChapterEvaluation = { - title, overall - } - - if (output_values_evaluation.output) { - evaluation.output = output_values_evaluation.output - } - - return { evaluation, evaluation_error: true } + if (message_segments.length > 0) evaluation_result.evaluation.message = message_segments.join('\n') + if (response.error) { evaluation_result.evaluation.error = response.error as Error } } + + return evaluation_result } -} +} \ No newline at end of file diff --git a/tools/src/tester/TestRunner.ts b/tools/src/tester/TestRunner.ts index 77fe2df6d..dad9bf06e 100644 --- a/tools/src/tester/TestRunner.ts +++ b/tools/src/tester/TestRunner.ts @@ -10,7 +10,6 @@ import type StoryEvaluator from './StoryEvaluator' import { StoryEvaluations, type StoryFile } from './types/eval.types' import fs from 'fs' -import { type Story } from './types/story.types' import { read_yaml } from '../helpers' import { Result } from './types/eval.types' import { type ResultLogger } from './ResultLogger' @@ -20,6 +19,7 @@ import { OpenSearchHttpClient } from 'OpenSearchHttpClient' import * as ansi from './Ansi' import _ from 'lodash' import { Logger } from 'Logger' +import StoryParser from './StoryParser' export default class TestRunner { private readonly _http_client: OpenSearchHttpClient @@ -77,7 +77,7 @@ export default class TestRunner { if (file.startsWith('.') || file == 'docker-compose.yml' || file == 'Dockerfile' || file.endsWith('.py')) { return [] } else if (fs.statSync(path).isFile()) { - const story: Story = read_yaml(path) + const story = StoryParser.parse(read_yaml(path)) return [{ display_path: next_prefix === '' ? basename(path) : next_prefix, full_path: path, diff --git a/tools/src/tester/types/eval.types.ts b/tools/src/tester/types/eval.types.ts index eacb2c62a..e65b80c3c 100644 --- a/tools/src/tester/types/eval.types.ts +++ b/tools/src/tester/types/eval.types.ts @@ -9,12 +9,12 @@ import { type ChapterOutput } from '../ChapterOutput' import { StoryOutputs } from '../StoryOutputs' -import type { Story } from "./story.types"; +import { ParsedStory } from './parsed_story.types'; export interface StoryFile { display_path: string full_path: string - story: Story + story: ParsedStory } export interface Operation { diff --git a/tools/src/tester/types/parsed_story.types.ts b/tools/src/tester/types/parsed_story.types.ts new file mode 100644 index 000000000..d0f6d0132 --- /dev/null +++ b/tools/src/tester/types/parsed_story.types.ts @@ -0,0 +1,17 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +import { Chapter, ChapterRequest, Story } from "./story.types" + +// a chapter with a single method +export type ParsedChapter = ChapterRequest & Chapter + +export interface ParsedStory extends Story { + chapters: ParsedChapter[] +} diff --git a/tools/src/tester/types/story.types.ts b/tools/src/tester/types/story.types.ts index 2cdc0a68d..78fc5ab3e 100644 --- a/tools/src/tester/types/story.types.ts +++ b/tools/src/tester/types/story.types.ts @@ -27,6 +27,11 @@ export type SupplementalChapter = ChapterRequest & { */ status?: number[]; }; +/** + * This interface was referenced by `Story`'s JSON-Schema + * via the `definition` "HttpMethod". + */ +export type HttpMethod = 'GET' | 'PUT' | 'POST' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; /** * This interface was referenced by `Story`'s JSON-Schema * via the `definition` "Parameter". @@ -80,6 +85,7 @@ export type Retry = { * via the `definition` "Chapter". */ export type Chapter = ChapterRequest & { + method?: HttpMethod[] | HttpMethod; /** * A brief description of the chapter. */ @@ -115,7 +121,7 @@ export interface ChapterRequest { */ id?: string; path: string; - method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'; + method: HttpMethod; parameters?: { [k: string]: Parameter; }; diff --git a/tools/tests/tester/ChapterEvaluator.test.ts b/tools/tests/tester/ChapterEvaluator.test.ts new file mode 100644 index 000000000..dc4da95a6 --- /dev/null +++ b/tools/tests/tester/ChapterEvaluator.test.ts @@ -0,0 +1,104 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +import axios from "axios"; +import { StoryOutputs } from 'tester/StoryOutputs'; +import { construct_tester_components } from './helpers' +import AxiosMockAdapter from "axios-mock-adapter"; +import { Result } from "tester/types/eval.types"; + +describe('ChapterEvaluator', () => { + var mock = new AxiosMockAdapter(axios) + const { chapter_evaluator } = construct_tester_components('tools/tests/tester/fixtures/specs/excerpt.yaml') + + afterEach(() => { + mock.reset() + }) + + test('instance', () => { + expect(chapter_evaluator).toBeDefined(); + }); + + describe('evaluate', () => { + const story_outputs = new StoryOutputs() + + test('a path not found in spec', async () => { + expect( + await chapter_evaluator.evaluate({ + synopsis: 'Perform a GET on an invalid path.', + path: '/invalid', + method: 'GET' + }, false, story_outputs)).toEqual( + { + title: 'Perform a GET on an invalid path.', + overall: { + result: Result.FAILED, + message: 'Operation "GET /invalid" not found in the spec.' + } + } + ) + }) + + test('a successful response', async () => { + mock.onAny().reply(200, '{"acknowledged":true}', { "content-type": "application/json" }) + + expect( + await chapter_evaluator.evaluate({ + synopsis: 'Perform a PUT /{index}.', + path: '/{index}', + method: 'PUT', + parameters: { + index: 'test' + }, + request: { + payload: {} + } + }, false, story_outputs)).toEqual( + { + title: 'Perform a PUT /{index}.', + path: 'PUT /{index}', + operation: { method: 'PUT', path: '/{index}' }, + request: { parameters: { index: { result: 'PASSED' } }, request: { result: 'PASSED' } }, + response: { output_values: { result: 'SKIPPED' }, payload_body: { result: 'PASSED' }, payload_schema: { result: 'PASSED' }, status: { result: 'PASSED' } }, + overall: { + result: Result.PASSED + } + } + ) + }) + + test('retries', async () => { + var count = 0 + + mock.onAny().reply((_config) => { + count += 1 + return [400, 'Bad Request'] + }) + + var result = await chapter_evaluator.evaluate({ + synopsis: 'Perform a PUT /{index}.', + path: '/{index}', + method: 'PUT', + retry: { + count: 4, + wait: 0 + }, + parameters: { + index: 'test' + }, + request: { + payload: {} + } + }, false, story_outputs) + + expect(result.overall.result).toEqual(Result.ERROR) + expect(count).toEqual(5) + }) + }) +}) diff --git a/tools/tests/tester/StoryParser.test.ts b/tools/tests/tester/StoryParser.test.ts new file mode 100644 index 000000000..607045571 --- /dev/null +++ b/tools/tests/tester/StoryParser.test.ts @@ -0,0 +1,31 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +import { read_yaml } from "helpers"; +import StoryParser from "../../src/tester/StoryParser"; +import _ from "lodash"; + +describe('StoryParser', () => { + const story = StoryParser.parse(read_yaml('tools/tests/tester/fixtures/stories/passed/multiple_methods.yaml')) + + test('does not expand prologues', () => { + expect(story.prologues?.length).toEqual(1) + expect(story.prologues && story.prologues[0].method).toEqual('HEAD') + }) + + test('does not expand epilogues', () => { + expect(story.epilogues?.length).toEqual(1) + expect(story.epilogues && story.epilogues[0].method).toEqual('DELETE') + }) + + test('expands chapters', () => { + expect(story.chapters?.length).toEqual(2) + expect(_.map(story.chapters, (chapter) => chapter.method)).toEqual(['PUT', 'HEAD']) + }) +}) \ No newline at end of file diff --git a/tools/tests/tester/StoryValidator.test.ts b/tools/tests/tester/StoryValidator.test.ts index 2d98159fa..2feee0189 100644 --- a/tools/tests/tester/StoryValidator.test.ts +++ b/tools/tests/tester/StoryValidator.test.ts @@ -10,12 +10,13 @@ import StoryValidator from "../../src/tester/StoryValidator"; import { StoryEvaluation } from "../../src/tester/types/eval.types"; import { read_yaml } from "../../src/helpers"; -import { Story } from "../../src/tester/types/story.types"; +import { ParsedStory } from "tester/types/parsed_story.types"; +import StoryParser from "../../src/tester/StoryParser"; const validator = new StoryValidator() function validate(path: string): StoryEvaluation | undefined { - const story: Story = read_yaml(path) + const story: ParsedStory = StoryParser.parse(read_yaml(path)) return validator.validate({ story, display_path: path, full_path: path }) } @@ -32,7 +33,8 @@ describe('StoryValidator', () => { expect(evaluation?.message).toBe("Invalid Story: " + "data/epilogues/0 contains unsupported properties: response --- " + "data/chapters/0 MUST contain the missing properties: method --- " + - "data/chapters/1/method MUST be equal to one of the allowed values: GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS") + "data/chapters/1/method MUST be equal to one of the allowed values: GET, PUT, POST, DELETE, PATCH, HEAD, OPTIONS --- " + + "data/chapters/1/method must be array --- data/chapters/1/method must match exactly one schema in oneOf") }) test('invalid description', () => { @@ -40,7 +42,7 @@ describe('StoryValidator', () => { 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]*\\.$\"") + "data/chapters/0/synopsis must match pattern \"^\\p{Lu}[\\s\\S]*\\.$|^\\p{Lu}[\\s\\S]*\\. \\[(GET|PUT|POST|DELETE|PATCH|HEAD|OPTIONS)\\]$\"") }) test('valid story', () => { diff --git a/tools/tests/tester/SupplementalChapterEvaluator.test.ts b/tools/tests/tester/SupplementalChapterEvaluator.test.ts new file mode 100644 index 000000000..56bb01eab --- /dev/null +++ b/tools/tests/tester/SupplementalChapterEvaluator.test.ts @@ -0,0 +1,90 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +*/ + +import axios from "axios"; +import { StoryOutputs } from 'tester/StoryOutputs'; +import { construct_tester_components } from './helpers' +import AxiosMockAdapter from "axios-mock-adapter"; +import { Result } from "tester/types/eval.types"; + +describe('SupplementalChapterEvaluator', () => { + var mock = new AxiosMockAdapter(axios) + const { supplemental_chapter_evaluator } = construct_tester_components('tools/tests/tester/fixtures/specs/excerpt.yaml') + + afterEach(() => { + mock.reset() + }) + + test('instance', () => { + expect(supplemental_chapter_evaluator).toBeDefined(); + }); + + describe('evaluate', () => { + const story_outputs = new StoryOutputs() + + test('a path not found in spec', async () => { + expect( + await supplemental_chapter_evaluator.evaluate({ + path: '/invalid', + method: 'GET' + }, story_outputs)).toEqual( + { + title: 'GET /invalid', + overall: { + result: Result.ERROR, + } + } + ) + }) + + test('a successful response', async () => { + mock.onAny().reply(200, '{"acknowledged":true}', { "content-type": "application/json" }) + + expect( + await supplemental_chapter_evaluator.evaluate({ + path: '/test', + method: 'PUT', + request: { + payload: {} + } + }, story_outputs)).toEqual( + { + title: 'PUT /test', + overall: { + result: Result.PASSED + } + } + ) + }) + + test('retries', async () => { + var count = 0 + + mock.onAny().reply((_config) => { + count += 1 + return [400, 'Bad Request'] + }) + + var result = await supplemental_chapter_evaluator.evaluate({ + path: '/test', + method: 'PUT', + retry: { + count: 4, + wait: 0 + }, + request: { + payload: {} + } + }, story_outputs) + + expect(result.overall.result).toEqual(Result.ERROR) + expect(count).toEqual(5) + }) + }) +}) diff --git a/tools/tests/tester/fixtures/evals/passed/multiple_methods.yaml b/tools/tests/tester/fixtures/evals/passed/multiple_methods.yaml new file mode 100644 index 000000000..7627ee448 --- /dev/null +++ b/tools/tests/tester/fixtures/evals/passed/multiple_methods.yaml @@ -0,0 +1,58 @@ +display_path: passed/multiple_methods.yaml +full_path: tools/tests/tester/fixtures/stories/passed/multiple_methods.yaml + +result: PASSED +description: This story has multiple methods in its chapters. +prologues: + - title: HEAD /movies + overall: + result: PASSED +chapters: + - title: Create and update index. [PUT] + path: 'PUT /{index}' + overall: + result: PASSED + operation: + method: PUT + path: /{index} + request: + parameters: + index: + result: PASSED + request: + result: PASSED + response: + output_values: + result: SKIPPED + payload_body: + result: PASSED + payload_schema: + result: PASSED + status: + result: PASSED + - title: Create and update index. [HEAD] + path: 'HEAD /{index}' + overall: + result: PASSED + operation: + method: HEAD + path: /{index} + request: + parameters: + index: + result: PASSED + request: + result: PASSED + response: + output_values: + result: SKIPPED + payload_body: + result: PASSED + payload_schema: + result: PASSED + status: + result: PASSED +epilogues: + - title: DELETE /movies + overall: + result: PASSED diff --git a/tools/tests/tester/fixtures/multiple_methods.yaml b/tools/tests/tester/fixtures/multiple_methods.yaml new file mode 100644 index 000000000..806c14b8d --- /dev/null +++ b/tools/tests/tester/fixtures/multiple_methods.yaml @@ -0,0 +1,17 @@ +$schema: ../../../../json_schemas/test_story.schema.yaml + +description: This story has multiple methods in its chapters. +prologues: + - path: /one + method: POST +epilogues: + - path: /one + method: PUT +chapters: + - synopsis: A PUT and POST method. + path: /{index} + method: + - POST + - PUT + parameters: + index: one diff --git a/tools/tests/tester/fixtures/specs/excerpt.yaml b/tools/tests/tester/fixtures/specs/excerpt.yaml index 6b28883ab..fcc19a666 100644 --- a/tools/tests/tester/fixtures/specs/excerpt.yaml +++ b/tools/tests/tester/fixtures/specs/excerpt.yaml @@ -179,8 +179,6 @@ components: type: boolean required: - acknowledged - - index - - shards_acknowledged parameters: cat.health::query.format: in: query diff --git a/tools/tests/tester/fixtures/stories/passed/multiple_methods.yaml b/tools/tests/tester/fixtures/stories/passed/multiple_methods.yaml new file mode 100644 index 000000000..56b4dfc4b --- /dev/null +++ b/tools/tests/tester/fixtures/stories/passed/multiple_methods.yaml @@ -0,0 +1,21 @@ +$schema: ../../../../../../json_schemas/test_story.schema.yaml + +description: This story has multiple methods in its chapters. +prologues: + - path: /movies + method: HEAD + status: + - 404 +epilogues: + - path: /movies + method: DELETE + status: + - 200 + - 404 +chapters: + - synopsis: Create and update index. + path: /{index} + # eslint-disable-next-line yml/sort-sequence-values + method: [PUT, HEAD] + parameters: + index: movies diff --git a/tools/tests/tester/helpers.ts b/tools/tests/tester/helpers.ts index 399c1359e..a8705bd2f 100644 --- a/tools/tests/tester/helpers.ts +++ b/tools/tests/tester/helpers.ts @@ -30,7 +30,8 @@ export function construct_tester_components (spec_path: string): { opensearch_http_client: OpenSearchHttpClient chapter_reader: ChapterReader schema_validator: SchemaValidator - chapter_evaluator: ChapterEvaluator + chapter_evaluator: ChapterEvaluator, + supplemental_chapter_evaluator: SupplementalChapterEvaluator, story_validator: StoryValidator story_evaluator: StoryEvaluator result_logger: ResultLogger @@ -62,6 +63,7 @@ export function construct_tester_components (spec_path: string): { chapter_reader, schema_validator, chapter_evaluator, + supplemental_chapter_evaluator, story_validator, story_evaluator, result_logger, diff --git a/tools/tests/tester/integ/TestRunner.test.ts b/tools/tests/tester/integ/TestRunner.test.ts index a747f1b64..8e2bfb353 100644 --- a/tools/tests/tester/integ/TestRunner.test.ts +++ b/tools/tests/tester/integ/TestRunner.test.ts @@ -34,6 +34,7 @@ test('stories folder', async () => { 'error/prologue_error', 'failed/invalid_data', 'failed/not_found', + 'passed/multiple_methods', 'passed/passed', 'passed/value_type', 'skipped/semver', diff --git a/tools/tests/tester/test.test.ts b/tools/tests/tester/test.test.ts index fd9949fc8..40bffa2fd 100644 --- a/tools/tests/tester/test.test.ts +++ b/tools/tests/tester/test.test.ts @@ -9,9 +9,10 @@ import { spawnSync } from 'child_process' import * as ansi from 'tester/Ansi' -import { type Chapter, type ChapterRequest, type Output, type Request, Story } from 'tester/types/story.types' +import { Chapter, ChapterRequest, type Output, type Request } from 'tester/types/story.types' import { ChapterEvaluation, Result, StoryEvaluation } from 'tester/types/eval.types' import StoryEvaluator from 'tester/StoryEvaluator' +import { ParsedStory } from 'tester/types/parsed_story.types' const spec = (args: string[]): any => { const start = spawnSync('ts-node', ['tools/src/tester/test.ts'].concat(args), { @@ -62,14 +63,14 @@ function dummy_chapter_request_with_input(parameters?: Record, requ function chapter(synopsis: string, request: ChapterRequest): Chapter { return { - synopsis, - ...request + ...request, + synopsis } } test('check_story_variables', () => { - const check_story_variables = (s: Story): StoryEvaluation | undefined => StoryEvaluator.check_story_variables(s, 'display_path', 'full_path') + const check_story_variables = (s: ParsedStory): StoryEvaluation | undefined => StoryEvaluator.check_story_variables(s, 'display_path', 'full_path') const failed = (prologues: ChapterEvaluation[] = [], chapters: ChapterEvaluation[] = []): StoryEvaluation => ({ result: Result.ERROR, description: 'story1',