@@ -1671,4 +1671,173 @@ pub struct Model {
16711671 output
16721672 ) ;
16731673 }
1674+
1675+ #[ test]
1676+ #[ serial]
1677+ fn test_generate_from_model_parent_stub_all_relation_types ( ) {
1678+ // Coverage for lines 114, 117, 120
1679+ // Tests: Parent stub generation with:
1680+ use tempfile:: TempDir ;
1681+
1682+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
1683+ let src_dir = temp_dir. path ( ) . join ( "src" ) ;
1684+ let models_dir = src_dir. join ( "models" ) ;
1685+ std:: fs:: create_dir_all ( & models_dir) . unwrap ( ) ;
1686+
1687+ // Create memo.rs with REQUIRED circular back-ref to user
1688+ // This triggers needs_parent_stub = true
1689+ let memo_model = r#"
1690+ pub struct Model {
1691+ pub id: i32,
1692+ pub title: String,
1693+ pub user_id: i32,
1694+ #[sea_orm(belongs_to = "super::user::Entity", from = "user_id")]
1695+ pub user: BelongsTo<super::user::Entity>,
1696+ }
1697+ "# ;
1698+ std:: fs:: write ( models_dir. join ( "memo.rs" ) , memo_model) . unwrap ( ) ;
1699+
1700+ // Create profile.rs (for optional single relation)
1701+ let profile_model = r#"
1702+ pub struct Model {
1703+ pub id: i32,
1704+ pub bio: String,
1705+ }
1706+ "# ;
1707+ std:: fs:: write ( models_dir. join ( "profile.rs" ) , profile_model) . unwrap ( ) ;
1708+
1709+ // Create settings.rs (for required single relation)
1710+ let settings_model = r#"
1711+ pub struct Model {
1712+ pub id: i32,
1713+ pub theme: String,
1714+ }
1715+ "# ;
1716+ std:: fs:: write ( models_dir. join ( "settings.rs" ) , settings_model) . unwrap ( ) ;
1717+
1718+ // Save and set CARGO_MANIFEST_DIR
1719+ let original_manifest_dir = std:: env:: var ( "CARGO_MANIFEST_DIR" ) . ok ( ) ;
1720+ unsafe { std:: env:: set_var ( "CARGO_MANIFEST_DIR" , temp_dir. path ( ) ) } ;
1721+
1722+ let new_type_name = syn:: Ident :: new ( "UserSchema" , proc_macro2:: Span :: call_site ( ) ) ;
1723+ let source_type: Type = syn:: parse_str ( "crate::models::user::Model" ) . unwrap ( ) ;
1724+
1725+ // Field mappings with various relation types
1726+ let field_mappings = vec ! [
1727+ // Regular field
1728+ (
1729+ syn:: Ident :: new( "id" , proc_macro2:: Span :: call_site( ) ) ,
1730+ syn:: Ident :: new( "id" , proc_macro2:: Span :: call_site( ) ) ,
1731+ false ,
1732+ false ,
1733+ ) ,
1734+ // HasMany (line 113) - this one triggers needs_parent_stub
1735+ (
1736+ syn:: Ident :: new( "memos" , proc_macro2:: Span :: call_site( ) ) ,
1737+ syn:: Ident :: new( "memos" , proc_macro2:: Span :: call_site( ) ) ,
1738+ false ,
1739+ true ,
1740+ ) ,
1741+ // Optional single relation (line 114)
1742+ (
1743+ syn:: Ident :: new( "profile" , proc_macro2:: Span :: call_site( ) ) ,
1744+ syn:: Ident :: new( "profile" , proc_macro2:: Span :: call_site( ) ) ,
1745+ false ,
1746+ true ,
1747+ ) ,
1748+ // Required single relation (line 117)
1749+ (
1750+ syn:: Ident :: new( "settings" , proc_macro2:: Span :: call_site( ) ) ,
1751+ syn:: Ident :: new( "settings" , proc_macro2:: Span :: call_site( ) ) ,
1752+ false ,
1753+ true ,
1754+ ) ,
1755+ // Relation field NOT in relation_fields (line 120)
1756+ (
1757+ syn:: Ident :: new( "orphan_rel" , proc_macro2:: Span :: call_site( ) ) ,
1758+ syn:: Ident :: new( "orphan_rel" , proc_macro2:: Span :: call_site( ) ) ,
1759+ false ,
1760+ true ,
1761+ ) ,
1762+ ] ;
1763+
1764+ // Relation fields - note: orphan_rel is NOT included here (hits line 120)
1765+ let relation_fields = vec ! [
1766+ // HasMany without inline_type_info (triggers needs_parent_stub)
1767+ create_test_relation_info(
1768+ "memos" ,
1769+ "HasMany" ,
1770+ quote! { crate :: models:: memo:: Schema } ,
1771+ false ,
1772+ ) ,
1773+ // Optional HasOne (hits line 114)
1774+ create_test_relation_info(
1775+ "profile" ,
1776+ "HasOne" ,
1777+ quote! { crate :: models:: profile:: Schema } ,
1778+ true , // optional
1779+ ) ,
1780+ // Required BelongsTo (hits line 117)
1781+ create_test_relation_info(
1782+ "settings" ,
1783+ "BelongsTo" ,
1784+ quote! { crate :: models:: settings:: Schema } ,
1785+ false , // required
1786+ ) ,
1787+ // Note: orphan_rel is NOT in relation_fields (hits line 120)
1788+ ] ;
1789+
1790+ let source_module_path = vec ! [
1791+ "crate" . to_string( ) ,
1792+ "models" . to_string( ) ,
1793+ "user" . to_string( ) ,
1794+ ] ;
1795+
1796+ let tokens = generate_from_model_with_relations (
1797+ & new_type_name,
1798+ & source_type,
1799+ & field_mappings,
1800+ & relation_fields,
1801+ & source_module_path,
1802+ & [ ] ,
1803+ ) ;
1804+
1805+ // Restore CARGO_MANIFEST_DIR
1806+ unsafe {
1807+ if let Some ( dir) = original_manifest_dir {
1808+ std:: env:: set_var ( "CARGO_MANIFEST_DIR" , dir) ;
1809+ } else {
1810+ std:: env:: remove_var ( "CARGO_MANIFEST_DIR" ) ;
1811+ }
1812+ }
1813+
1814+ let output = tokens. to_string ( ) ;
1815+ assert ! ( output. contains( "impl UserSchema" ) ) ;
1816+ // Should have parent stub (line 307)
1817+ assert ! (
1818+ output. contains( "__parent_stub__" ) ,
1819+ "Should have parent stub: {}" ,
1820+ output
1821+ ) ;
1822+ // Parent stub should have various default values
1823+ // Line 113: memos: vec![]
1824+ assert ! (
1825+ output. contains( "memos : vec ! []" ) ,
1826+ "Should have memos: vec![]: {}" ,
1827+ output
1828+ ) ;
1829+ // Line 114 & 117: profile/settings: None (both optional and required single relations)
1830+ // (Both produce None in parent stub)
1831+ assert ! (
1832+ output. contains( "profile : None" ) || output. contains( "settings : None" ) ,
1833+ "Should have None for single relations: {}" ,
1834+ output
1835+ ) ;
1836+ // Line 120: orphan_rel: Default::default()
1837+ assert ! (
1838+ output. contains( "Default :: default ()" ) ,
1839+ "Should have Default::default() for orphan: {}" ,
1840+ output
1841+ ) ;
1842+ }
16741843}
0 commit comments