use serde_json::json; // ============================================================================ // M3.5.5 — GET /skills and /skills/{name}: skills catalog // ============================================================================ // // 8 tests covering: // - List all loadable skills (exclude drafts) // - Skill metadata fields // - Individual skill detail // - Include body parameter // - Filter by promoted status // - Filter by generated status // - Admin sees drafts // - Skill counts match // #[test] fn s1_list_skills_excludes_drafts() { // GET /memory/skills should NOT include drafts // Drafts are in _drafts/ folder let skills_list = json!({ "skills": [ { "name": "infra-root-causes", "description": "Identify root causes of infrastructure failures", "promoted_at": "2026-08-20T10:30:00Z" }, { "name": "ci-triage", "description": "CI/CD failure diagnosis", "promoted_at": "2026-08-19T14:22:00Z" } ] }); let skills = skills_list["skills"].as_array().unwrap(); assert_eq!(skills.len(), 2, "Should list promoted skills only"); // Verify no draft names for skill in skills { let name = skill["name"].as_str().unwrap(); assert!(!name.contains("_draft"), "Name should not indicate draft status"); } } #[test] fn s2_skill_metadata_fields_are_complete() { // Skill metadata must include: // - name // - description // - when_to_use // - argument_hint // - promoted_at (ISO 8601 timestamp) // - generated_from (null or skill name) let skill = json!({ "name": "infra-root-causes", "description": "Identify root causes of infrastructure failures", "when_to_use": "When troubleshooting cluster or service outages", "argument_hint": "--project ", "promoted_at": "2026-08-20T10:30:00Z", "generated_from": null }); assert!(skill["name"].is_string()); assert!(skill["description"].is_string()); assert!(skill["when_to_use"].is_string()); assert!(skill["argument_hint"].is_string()); assert!(skill["promoted_at"].is_string()); assert!(skill["generated_from"].is_null() || skill["generated_from"].is_string()); } #[test] fn s3_individual_skill_detail() { // GET /memory/skills/infra-root-causes // Returns metadata only (not body by default) let skill = json!({ "name": "infra-root-causes", "description": "Identify root causes of infrastructure failures", "when_to_use": "When troubleshooting cluster or service outages", "argument_hint": "--project ", "promoted_at": "2026-08-20T10:30:00Z", "generated_from": null }); assert_eq!(skill["name"], "infra-root-causes"); assert!(!skill.get("body").is_some(), "Should not include body by default"); } #[test] fn s4_skill_with_body_parameter() { // GET /memory/skills/infra-root-causes?include_body=true // Should include full content let skill = json!({ "name": "infra-root-causes", "description": "Identify root causes of infrastructure failures", "body": "# Infrastructure Root Causes\n\n## Cluster failures\n\n..." }); assert!(skill["body"].is_string()); let body = skill["body"].as_str().unwrap(); assert!(body.contains("# Infrastructure Root Causes")); } #[test] fn s5_filter_by_promoted_status() { // GET /memory/skills?loadable=true // loadable=true: only promoted skills // loadable=false: only drafts (admin) let promoted_skills = json!({ "skills": [ {"name": "infra-root-causes", "promoted_at": "2026-08-20T10:30:00Z"}, {"name": "ci-triage", "promoted_at": "2026-08-19T14:22:00Z"} ] }); for skill in promoted_skills["skills"].as_array().unwrap() { assert!( skill["promoted_at"].is_string(), "Promoted skills must have promoted_at" ); } } #[test] fn s6_filter_by_generated_status() { // GET /memory/skills?generated=false // generated=false: handwritten skills // generated=true: derived from lessons let handwritten = json!({ "skills": [ { "name": "infra-root-causes", "generated_from": null } ] }); let generated = json!({ "skills": [ { "name": "lesson-npm-conflict", "generated_from": "lesson-id-xyz" } ] }); let hw_skill = &handwritten["skills"][0]; assert!(hw_skill["generated_from"].is_null()); let gen_skill = &generated["skills"][0]; assert!(gen_skill["generated_from"].is_string()); } #[test] fn s7_admin_sees_drafts() { // With admin apikey, GET /memory/skills?loadable=false // Returns draft skills from _drafts/ folder let draft_skills = json!({ "skills": [ { "name": "draft-experimental-feature", "description": "WIP: experimental feature diagnosis", "promoted_at": null, "is_draft": true } ] }); let skill = &draft_skills["skills"][0]; assert!(skill["is_draft"].as_bool().unwrap_or(false)); assert!(skill["promoted_at"].is_null(), "Drafts have no promoted_at"); } #[test] fn s8_skill_count_metadata() { // Response should include skill count let response = json!({ "skills": [ {"name": "skill1"}, {"name": "skill2"}, {"name": "skill3"} ], "total_count": 3, "loaded_at": "2026-08-23T16:30:00Z" }); let skills = response["skills"].as_array().unwrap(); let count = response["total_count"].as_u64().unwrap(); assert_eq!(skills.len(), count as usize); }