use outline::models::{Ability, DocumentTasks, NavigationNode, Policy, User, UserRole}; use serde_json::json; #[test] fn deserializes_full_document_fixture() { let raw = include_str!("fixtures/document.json"); let document: outline::models::Document = serde_json::from_str(raw).unwrap(); assert_eq!(document.title, "Welcome to Acme Inc"); assert_eq!(document.url_id.as_deref(), Some("hDYep1TPAM")); assert_eq!( document.tasks, Some(DocumentTasks { completed: 1, total: 4 }) ); assert!(document.parent_document_id.is_none()); assert!(document.archived_at.is_none()); assert_eq!(document.created_by.unwrap().name, "Jane Doe"); } /// A server (self-hosted, newer than this crate) adding a brand new field to /// a response must not break deserialization of the rest of the object. #[test] fn unknown_top_level_field_is_ignored() { let raw = json!({ "id": "doc-1", "title": "Still works", "aBrandNewFieldFromTheFuture": { "nested": true }, }); let document: outline::models::Document = serde_json::from_value(raw).unwrap(); assert_eq!(document.title, "Still works"); } /// A server returning a role value this crate doesn't know about yet must /// round-trip instead of failing to deserialize the whole `User`. #[test] fn unknown_enum_value_round_trips_via_unknown_variant() { let raw = json!({ "id": "user-1", "name": "Future User", "role": "super_admin", }); let user: User = serde_json::from_value(raw).unwrap(); match &user.role { Some(UserRole::Unknown(value)) => assert_eq!(value.0, "super_admin"), other => panic!("expected UserRole::Unknown, got {other:?}"), } let round_tripped = serde_json::to_value(&user).unwrap(); assert_eq!(round_tripped["role"], json!("super_admin")); } #[test] fn known_enum_value_deserializes_normally() { let raw = json!({ "id": "user-1", "name": "Jane", "role": "member" }); let user: User = serde_json::from_value(raw).unwrap(); assert_eq!(user.role, Some(UserRole::Member)); } #[test] fn policy_ability_accepts_boolean_or_membership_list() { let raw = json!({ "id": "doc-1", "abilities": { "update": true, "delete": false, "restrictedUpdate": ["group-a", "group-b"], } }); let policy: Policy = serde_json::from_value(raw).unwrap(); assert!(policy.can("update")); assert!(!policy.can("delete")); assert!(policy.can("restrictedUpdate")); assert!(!policy.can("missingAbility")); assert_eq!( policy.abilities["restrictedUpdate"], Ability::Memberships(vec!["group-a".into(), "group-b".into()]) ); } #[test] fn navigation_node_deserializes_recursively() { let raw = json!({ "id": "doc-1", "title": "Parent", "children": [ { "id": "doc-2", "title": "Child", "children": [ { "id": "doc-3", "title": "Grandchild" } ]} ] }); let node: NavigationNode = serde_json::from_value(raw).unwrap(); assert_eq!(node.children.len(), 1); assert_eq!(node.children[0].children[0].title, "Grandchild"); }