244 lines
6.8 KiB
Rust
244 lines
6.8 KiB
Rust
mod unit {
|
|
use crank_core::HttpMethod;
|
|
use crank_import::rest::preview_document;
|
|
|
|
const OPENAPI3: &str = r#"
|
|
openapi: 3.0.3
|
|
info:
|
|
title: Frankfurter API
|
|
servers:
|
|
- url: https://api.frankfurter.dev
|
|
paths:
|
|
/v2/latest:
|
|
get:
|
|
operationId: getLatestRates
|
|
summary: Получить последние курсы
|
|
description: Возвращает последние курсы валют для базовой валюты.
|
|
tags: [currency]
|
|
parameters:
|
|
- name: base
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
- name: symbols
|
|
in: query
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [amount, base]
|
|
properties:
|
|
amount:
|
|
type: number
|
|
base:
|
|
type: string
|
|
"#;
|
|
|
|
const SWAGGER2: &str = r#"
|
|
swagger: "2.0"
|
|
info:
|
|
title: Pet API
|
|
host: petstore.example.com
|
|
basePath: /api
|
|
schemes: [https]
|
|
paths:
|
|
/pets/{id}:
|
|
get:
|
|
operationId: getPet
|
|
summary: Получить питомца
|
|
tags: [pets]
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
schema:
|
|
$ref: '#/definitions/Pet'
|
|
definitions:
|
|
Pet:
|
|
type: object
|
|
required: [id, name]
|
|
properties:
|
|
id:
|
|
type: string
|
|
name:
|
|
type: string
|
|
"#;
|
|
|
|
#[test]
|
|
fn previews_openapi3_rest_operations_grouped_by_tag() {
|
|
let preview = preview_document(OPENAPI3).unwrap();
|
|
|
|
assert_eq!(preview.source.format, "openapi");
|
|
assert_eq!(preview.source.servers, vec!["https://api.frankfurter.dev"]);
|
|
assert_eq!(preview.groups.len(), 1);
|
|
assert_eq!(preview.groups[0].key, "currency");
|
|
let operation = &preview.groups[0].operations[0];
|
|
assert_eq!(operation.method, HttpMethod::Get);
|
|
assert_eq!(operation.suggested_name, "get_latest_rates");
|
|
assert_eq!(operation.input_fields, 2);
|
|
assert_eq!(operation.output_fields, 2);
|
|
assert_eq!(operation.draft.target.path_template, "/v2/latest");
|
|
assert_eq!(operation.draft.input_mapping.rules.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn previews_swagger2_and_resolves_definitions() {
|
|
let preview = preview_document(SWAGGER2).unwrap();
|
|
|
|
assert_eq!(preview.source.format, "swagger");
|
|
assert_eq!(
|
|
preview.source.servers,
|
|
vec!["https://petstore.example.com/api"]
|
|
);
|
|
let operation = &preview.groups[0].operations[0];
|
|
assert_eq!(operation.suggested_name, "get_pet");
|
|
assert_eq!(operation.input_fields, 1);
|
|
assert_eq!(operation.output_fields, 2);
|
|
assert_eq!(operation.draft.target.path_template, "/pets/{id}");
|
|
assert_eq!(
|
|
operation.draft.input_mapping.rules[0].target,
|
|
"$.request.path.id"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn reports_missing_descriptions_as_recommendations() {
|
|
let document = r#"
|
|
openapi: 3.0.3
|
|
info: { title: Minimal API }
|
|
paths:
|
|
/items:
|
|
get:
|
|
responses:
|
|
'204': { description: Empty }
|
|
"#;
|
|
|
|
let preview = preview_document(document).unwrap();
|
|
let operation = &preview.groups[0].operations[0];
|
|
let codes = operation
|
|
.findings
|
|
.iter()
|
|
.map(|finding| finding.code.as_str())
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(codes.contains(&"missing_operation_id"));
|
|
assert!(codes.contains(&"missing_summary"));
|
|
assert!(codes.contains(&"missing_description"));
|
|
assert!(codes.contains(&"missing_response_schema"));
|
|
}
|
|
|
|
#[test]
|
|
fn expands_json_request_body_object_into_tool_inputs() {
|
|
let document = r#"
|
|
openapi: 3.0.3
|
|
info: { title: CRM API }
|
|
servers:
|
|
- url: https://crm.example.test
|
|
paths:
|
|
/leads:
|
|
post:
|
|
operationId: createLead
|
|
summary: Создать лид
|
|
description: Создает лид в CRM.
|
|
tags: [crm]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [email]
|
|
properties:
|
|
email: { type: string }
|
|
name: { type: string }
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
"#;
|
|
|
|
let preview = preview_document(document).unwrap();
|
|
let operation = &preview.groups[0].operations[0];
|
|
let input_fields = &operation.draft.input_schema.fields;
|
|
let targets = operation
|
|
.draft
|
|
.input_mapping
|
|
.rules
|
|
.iter()
|
|
.map(|rule| rule.target.as_str())
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(input_fields.contains_key("email"));
|
|
assert!(input_fields.contains_key("name"));
|
|
assert!(targets.contains(&"$.request.body.email"));
|
|
assert!(targets.contains(&"$.request.body.name"));
|
|
}
|
|
|
|
#[test]
|
|
fn reports_tool_quality_recommendations_for_imported_operations() {
|
|
let document = r#"
|
|
openapi: 3.0.3
|
|
info: { title: Wide API }
|
|
paths:
|
|
/items:
|
|
get:
|
|
operationId: getItems
|
|
summary: Get items
|
|
description: Get items.
|
|
parameters:
|
|
- name: page
|
|
in: query
|
|
schema: { type: integer }
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
field01: { type: string }
|
|
field02: { type: string }
|
|
field03: { type: string }
|
|
field04: { type: string }
|
|
field05: { type: string }
|
|
field06: { type: string }
|
|
field07: { type: string }
|
|
field08: { type: string }
|
|
field09: { type: string }
|
|
field10: { type: string }
|
|
field11: { type: string }
|
|
field12: { type: string }
|
|
field13: { type: string }
|
|
"#;
|
|
|
|
let preview = preview_document(document).unwrap();
|
|
let operation = &preview.groups[0].operations[0];
|
|
let codes = operation
|
|
.findings
|
|
.iter()
|
|
.map(|finding| finding.code.as_str())
|
|
.collect::<Vec<_>>();
|
|
|
|
assert!(codes.contains(&"parameter_descriptions_missing"));
|
|
assert!(codes.contains(&"weak_tool_description"));
|
|
assert!(codes.contains(&"weak_tool_name"));
|
|
assert!(codes.contains(&"too_many_output_fields"));
|
|
}
|
|
}
|