Skip to content

Commit d638dd4

Browse files
committed
Add testcase
1 parent 86d8fd2 commit d638dd4

6 files changed

Lines changed: 479 additions & 21 deletions

File tree

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vespera_core/src/openapi.rs

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,317 @@ impl OpenApi {
200200
Ok(())
201201
}
202202
}
203+
204+
#[cfg(test)]
205+
mod tests {
206+
use super::*;
207+
use crate::route::{Operation, PathItem};
208+
use crate::schema::{Components, Schema, SchemaType, SecurityScheme, SecuritySchemeType};
209+
210+
fn create_base_openapi() -> OpenApi {
211+
OpenApi {
212+
openapi: OpenApiVersion::V3_1_0,
213+
info: Info {
214+
title: "Base API".to_string(),
215+
version: "1.0.0".to_string(),
216+
description: None,
217+
terms_of_service: None,
218+
contact: None,
219+
license: None,
220+
summary: None,
221+
},
222+
servers: None,
223+
paths: BTreeMap::new(),
224+
components: None,
225+
security: None,
226+
tags: None,
227+
external_docs: None,
228+
}
229+
}
230+
231+
fn create_path_item(summary: &str) -> PathItem {
232+
PathItem {
233+
get: Some(Operation {
234+
summary: Some(summary.to_string()),
235+
description: None,
236+
operation_id: None,
237+
tags: None,
238+
parameters: None,
239+
request_body: None,
240+
responses: BTreeMap::new(),
241+
security: None,
242+
}),
243+
post: None,
244+
put: None,
245+
delete: None,
246+
patch: None,
247+
options: None,
248+
head: None,
249+
trace: None,
250+
parameters: None,
251+
summary: None,
252+
description: None,
253+
}
254+
}
255+
256+
#[test]
257+
fn test_merge_paths() {
258+
let mut base = create_base_openapi();
259+
base.paths
260+
.insert("/users".to_string(), create_path_item("Get users"));
261+
262+
let mut other = create_base_openapi();
263+
other
264+
.paths
265+
.insert("/posts".to_string(), create_path_item("Get posts"));
266+
other
267+
.paths
268+
.insert("/users".to_string(), create_path_item("Other users")); // Conflict
269+
270+
base.merge(other);
271+
272+
// Both paths should exist
273+
assert!(base.paths.contains_key("/users"));
274+
assert!(base.paths.contains_key("/posts"));
275+
// Self takes precedence on conflict
276+
assert_eq!(
277+
base.paths
278+
.get("/users")
279+
.unwrap()
280+
.get
281+
.as_ref()
282+
.unwrap()
283+
.summary,
284+
Some("Get users".to_string())
285+
);
286+
}
287+
288+
#[test]
289+
fn test_merge_schemas() {
290+
let mut base = create_base_openapi();
291+
let mut base_schemas = BTreeMap::new();
292+
base_schemas.insert("User".to_string(), Schema::object());
293+
base.components = Some(Components {
294+
schemas: Some(base_schemas),
295+
responses: None,
296+
parameters: None,
297+
examples: None,
298+
request_bodies: None,
299+
headers: None,
300+
security_schemes: None,
301+
});
302+
303+
let mut other = create_base_openapi();
304+
let mut other_schemas = BTreeMap::new();
305+
other_schemas.insert("Post".to_string(), Schema::object());
306+
other_schemas.insert("User".to_string(), Schema::string()); // Conflict
307+
other.components = Some(Components {
308+
schemas: Some(other_schemas),
309+
responses: None,
310+
parameters: None,
311+
examples: None,
312+
request_bodies: None,
313+
headers: None,
314+
security_schemes: None,
315+
});
316+
317+
base.merge(other);
318+
319+
let schemas = base.components.as_ref().unwrap().schemas.as_ref().unwrap();
320+
assert!(schemas.contains_key("User"));
321+
assert!(schemas.contains_key("Post"));
322+
// Self takes precedence on conflict
323+
assert_eq!(
324+
schemas.get("User").unwrap().schema_type,
325+
Some(SchemaType::Object)
326+
);
327+
}
328+
329+
#[test]
330+
fn test_merge_schemas_when_self_has_no_components() {
331+
let mut base = create_base_openapi();
332+
assert!(base.components.is_none());
333+
334+
let mut other = create_base_openapi();
335+
let mut other_schemas = BTreeMap::new();
336+
other_schemas.insert("Post".to_string(), Schema::object());
337+
other.components = Some(Components {
338+
schemas: Some(other_schemas),
339+
responses: None,
340+
parameters: None,
341+
examples: None,
342+
request_bodies: None,
343+
headers: None,
344+
security_schemes: None,
345+
});
346+
347+
base.merge(other);
348+
349+
assert!(base.components.is_some());
350+
let schemas = base.components.as_ref().unwrap().schemas.as_ref().unwrap();
351+
assert!(schemas.contains_key("Post"));
352+
}
353+
354+
#[test]
355+
fn test_merge_security_schemes() {
356+
let mut base = create_base_openapi();
357+
let mut base_security_schemes = HashMap::new();
358+
base_security_schemes.insert(
359+
"bearerAuth".to_string(),
360+
SecurityScheme {
361+
r#type: SecuritySchemeType::Http,
362+
description: None,
363+
name: None,
364+
r#in: None,
365+
scheme: Some("bearer".to_string()),
366+
bearer_format: Some("JWT".to_string()),
367+
},
368+
);
369+
base.components = Some(Components {
370+
schemas: None,
371+
responses: None,
372+
parameters: None,
373+
examples: None,
374+
request_bodies: None,
375+
headers: None,
376+
security_schemes: Some(base_security_schemes),
377+
});
378+
379+
let mut other = create_base_openapi();
380+
let mut other_security_schemes = HashMap::new();
381+
other_security_schemes.insert(
382+
"apiKey".to_string(),
383+
SecurityScheme {
384+
r#type: SecuritySchemeType::ApiKey,
385+
description: None,
386+
name: Some("X-API-Key".to_string()),
387+
r#in: Some("header".to_string()),
388+
scheme: None,
389+
bearer_format: None,
390+
},
391+
);
392+
other.components = Some(Components {
393+
schemas: None,
394+
responses: None,
395+
parameters: None,
396+
examples: None,
397+
request_bodies: None,
398+
headers: None,
399+
security_schemes: Some(other_security_schemes),
400+
});
401+
402+
base.merge(other);
403+
404+
let security_schemes = base
405+
.components
406+
.as_ref()
407+
.unwrap()
408+
.security_schemes
409+
.as_ref()
410+
.unwrap();
411+
assert!(security_schemes.contains_key("bearerAuth"));
412+
assert!(security_schemes.contains_key("apiKey"));
413+
}
414+
415+
#[test]
416+
fn test_merge_tags() {
417+
let mut base = create_base_openapi();
418+
base.tags = Some(vec![Tag {
419+
name: "users".to_string(),
420+
description: Some("User operations".to_string()),
421+
external_docs: None,
422+
}]);
423+
424+
let mut other = create_base_openapi();
425+
other.tags = Some(vec![
426+
Tag {
427+
name: "posts".to_string(),
428+
description: Some("Post operations".to_string()),
429+
external_docs: None,
430+
},
431+
Tag {
432+
name: "users".to_string(),
433+
description: Some("Duplicate users tag".to_string()),
434+
external_docs: None,
435+
}, // Duplicate
436+
]);
437+
438+
base.merge(other);
439+
440+
let tags = base.tags.as_ref().unwrap();
441+
assert_eq!(tags.len(), 2); // No duplicates
442+
assert!(tags.iter().any(|t| t.name == "users"));
443+
assert!(tags.iter().any(|t| t.name == "posts"));
444+
// Self's description takes precedence
445+
let users_tag = tags.iter().find(|t| t.name == "users").unwrap();
446+
assert_eq!(users_tag.description, Some("User operations".to_string()));
447+
}
448+
449+
#[test]
450+
fn test_merge_tags_when_self_has_none() {
451+
let mut base = create_base_openapi();
452+
assert!(base.tags.is_none());
453+
454+
let mut other = create_base_openapi();
455+
other.tags = Some(vec![Tag {
456+
name: "posts".to_string(),
457+
description: None,
458+
external_docs: None,
459+
}]);
460+
461+
base.merge(other);
462+
463+
assert!(base.tags.is_some());
464+
assert_eq!(base.tags.as_ref().unwrap().len(), 1);
465+
}
466+
467+
#[test]
468+
fn test_merge_from_str() {
469+
let mut base = create_base_openapi();
470+
base.paths
471+
.insert("/users".to_string(), create_path_item("Get users"));
472+
473+
let other_json = r#"{
474+
"openapi": "3.1.0",
475+
"info": { "title": "Other API", "version": "2.0.0" },
476+
"paths": {
477+
"/posts": { "get": { "summary": "Get posts", "responses": {} } }
478+
}
479+
}"#;
480+
481+
let result = base.merge_from_str(other_json);
482+
assert!(result.is_ok());
483+
assert!(base.paths.contains_key("/users"));
484+
assert!(base.paths.contains_key("/posts"));
485+
}
486+
487+
#[test]
488+
fn test_merge_from_str_invalid_json() {
489+
let mut base = create_base_openapi();
490+
let invalid_json = "{ invalid json }";
491+
492+
let result = base.merge_from_str(invalid_json);
493+
assert!(result.is_err());
494+
}
495+
496+
#[test]
497+
fn test_merge_empty_other() {
498+
let mut base = create_base_openapi();
499+
base.paths
500+
.insert("/users".to_string(), create_path_item("Get users"));
501+
base.tags = Some(vec![Tag {
502+
name: "users".to_string(),
503+
description: None,
504+
external_docs: None,
505+
}]);
506+
507+
let other = create_base_openapi(); // Empty paths, no components, no tags
508+
509+
base.merge(other);
510+
511+
// Base should remain unchanged
512+
assert_eq!(base.paths.len(), 1);
513+
assert!(base.paths.contains_key("/users"));
514+
assert_eq!(base.tags.as_ref().unwrap().len(), 1);
515+
}
516+
}

0 commit comments

Comments
 (0)