@@ -1756,4 +1756,263 @@ def dummy_func(**kwargs):
17561756 )
17571757
17581758
1759+ def test_one_of_discriminator_polymorphism ():
1760+ """Test that oneOf with discriminator creates proper polymorphic union types.
1761+
1762+ Tests that oneOf + discriminator patterns are properly converted to Pydantic discriminated unions.
1763+ """
1764+ schema = {
1765+ "$defs" : {
1766+ "CreateProject" : {
1767+ "description" : "Action: Create an Azure DevOps project." ,
1768+ "properties" : {
1769+ "name" : {
1770+ "const" : "create_project" ,
1771+ "default" : "create_project" ,
1772+ "type" : "string" ,
1773+ },
1774+ "params" : {"$ref" : "#/$defs/CreateProjectParams" },
1775+ },
1776+ "required" : ["params" ],
1777+ "type" : "object" ,
1778+ },
1779+ "CreateProjectParams" : {
1780+ "description" : "Parameters for the create_project action." ,
1781+ "properties" : {
1782+ "orgUrl" : {"minLength" : 1 , "type" : "string" },
1783+ "projectName" : {"minLength" : 1 , "type" : "string" },
1784+ "description" : {"default" : "" , "type" : "string" },
1785+ "template" : {"default" : "Agile" , "type" : "string" },
1786+ "sourceControl" : {
1787+ "default" : "Git" ,
1788+ "enum" : ["Git" , "Tfvc" ],
1789+ "type" : "string" ,
1790+ },
1791+ "visibility" : {"default" : "private" , "type" : "string" },
1792+ },
1793+ "required" : ["orgUrl" , "projectName" ],
1794+ "type" : "object" ,
1795+ },
1796+ "DeployRequest" : {
1797+ "description" : "Request to deploy Azure DevOps resources." ,
1798+ "properties" : {
1799+ "projectName" : {"minLength" : 1 , "type" : "string" },
1800+ "organization" : {"minLength" : 1 , "type" : "string" },
1801+ "actions" : {
1802+ "items" : {
1803+ "discriminator" : {
1804+ "mapping" : {
1805+ "create_project" : "#/$defs/CreateProject" ,
1806+ "hello_world" : "#/$defs/HelloWorld" ,
1807+ },
1808+ "propertyName" : "name" ,
1809+ },
1810+ "oneOf" : [
1811+ {"$ref" : "#/$defs/HelloWorld" },
1812+ {"$ref" : "#/$defs/CreateProject" },
1813+ ],
1814+ },
1815+ "type" : "array" ,
1816+ },
1817+ },
1818+ "required" : ["projectName" , "organization" ],
1819+ "type" : "object" ,
1820+ },
1821+ "HelloWorld" : {
1822+ "description" : "Action: Prints a greeting message." ,
1823+ "properties" : {
1824+ "name" : {
1825+ "const" : "hello_world" ,
1826+ "default" : "hello_world" ,
1827+ "type" : "string" ,
1828+ },
1829+ "params" : {"$ref" : "#/$defs/HelloWorldParams" },
1830+ },
1831+ "required" : ["params" ],
1832+ "type" : "object" ,
1833+ },
1834+ "HelloWorldParams" : {
1835+ "description" : "Parameters for the hello_world action." ,
1836+ "properties" : {
1837+ "name" : {
1838+ "description" : "Name to greet" ,
1839+ "minLength" : 1 ,
1840+ "type" : "string" ,
1841+ }
1842+ },
1843+ "required" : ["name" ],
1844+ "type" : "object" ,
1845+ },
1846+ },
1847+ "properties" : {"params" : {"$ref" : "#/$defs/DeployRequest" }},
1848+ "required" : ["params" ],
1849+ "type" : "object" ,
1850+ }
1851+
1852+ # Build the model
1853+ model = _build_pydantic_model_from_json_schema ("deploy_tool" , schema )
1854+
1855+ # Verify the model structure
1856+ assert model is not None
1857+ assert issubclass (model , BaseModel )
1858+
1859+ # Test with HelloWorld action
1860+ hello_world_data = {
1861+ "params" : {
1862+ "projectName" : "MyProject" ,
1863+ "organization" : "MyOrg" ,
1864+ "actions" : [
1865+ {
1866+ "name" : "hello_world" ,
1867+ "params" : {"name" : "Alice" },
1868+ }
1869+ ],
1870+ }
1871+ }
1872+
1873+ instance = model (** hello_world_data )
1874+ assert instance .params .projectName == "MyProject"
1875+ assert instance .params .organization == "MyOrg"
1876+ assert len (instance .params .actions ) == 1
1877+ assert instance .params .actions [0 ].name == "hello_world"
1878+ assert instance .params .actions [0 ].params .name == "Alice"
1879+
1880+ # Test with CreateProject action
1881+ create_project_data = {
1882+ "params" : {
1883+ "projectName" : "MyProject" ,
1884+ "organization" : "MyOrg" ,
1885+ "actions" : [
1886+ {
1887+ "name" : "create_project" ,
1888+ "params" : {
1889+ "orgUrl" : "https://dev.azure.com/myorg" ,
1890+ "projectName" : "NewProject" ,
1891+ "sourceControl" : "Git" ,
1892+ },
1893+ }
1894+ ],
1895+ }
1896+ }
1897+
1898+ instance2 = model (** create_project_data )
1899+ assert instance2 .params .actions [0 ].name == "create_project"
1900+ assert instance2 .params .actions [0 ].params .projectName == "NewProject"
1901+ assert instance2 .params .actions [0 ].params .sourceControl == "Git"
1902+
1903+ # Test with mixed actions
1904+ mixed_data = {
1905+ "params" : {
1906+ "projectName" : "MyProject" ,
1907+ "organization" : "MyOrg" ,
1908+ "actions" : [
1909+ {"name" : "hello_world" , "params" : {"name" : "Bob" }},
1910+ {
1911+ "name" : "create_project" ,
1912+ "params" : {
1913+ "orgUrl" : "https://dev.azure.com/myorg" ,
1914+ "projectName" : "AnotherProject" ,
1915+ },
1916+ },
1917+ ],
1918+ }
1919+ }
1920+
1921+ instance3 = model (** mixed_data )
1922+ assert len (instance3 .params .actions ) == 2
1923+ assert instance3 .params .actions [0 ].name == "hello_world"
1924+ assert instance3 .params .actions [1 ].name == "create_project"
1925+
1926+
1927+ def test_const_creates_literal ():
1928+ """Test that const in JSON Schema creates Literal type."""
1929+ schema = {
1930+ "properties" : {
1931+ "action" : {
1932+ "const" : "create" ,
1933+ "type" : "string" ,
1934+ "description" : "Action type" ,
1935+ },
1936+ "value" : {"type" : "integer" },
1937+ },
1938+ "required" : ["action" , "value" ],
1939+ }
1940+
1941+ model = _build_pydantic_model_from_json_schema ("test_const" , schema )
1942+
1943+ # Verify valid const value works
1944+ instance = model (action = "create" , value = 42 )
1945+ assert instance .action == "create"
1946+ assert instance .value == 42
1947+
1948+ # Verify incorrect const value fails
1949+ with pytest .raises (ValidationError ):
1950+ model (action = "delete" , value = 42 )
1951+
1952+
1953+ def test_enum_creates_literal ():
1954+ """Test that enum in JSON Schema creates Literal type."""
1955+ schema = {
1956+ "properties" : {
1957+ "status" : {
1958+ "enum" : ["pending" , "approved" , "rejected" ],
1959+ "type" : "string" ,
1960+ "description" : "Status" ,
1961+ },
1962+ "priority" : {"enum" : [1 , 2 , 3 ], "type" : "integer" },
1963+ },
1964+ "required" : ["status" ],
1965+ }
1966+
1967+ model = _build_pydantic_model_from_json_schema ("test_enum" , schema )
1968+
1969+ # Verify valid enum values work
1970+ instance = model (status = "approved" , priority = 2 )
1971+ assert instance .status == "approved"
1972+ assert instance .priority == 2
1973+
1974+ # Verify invalid enum value fails
1975+ with pytest .raises (ValidationError ):
1976+ model (status = "unknown" )
1977+
1978+ with pytest .raises (ValidationError ):
1979+ model (status = "pending" , priority = 5 )
1980+
1981+
1982+ def test_nested_object_with_const_and_enum ():
1983+ """Test that const and enum work in nested objects."""
1984+ schema = {
1985+ "properties" : {
1986+ "config" : {
1987+ "type" : "object" ,
1988+ "properties" : {
1989+ "type" : {
1990+ "const" : "production" ,
1991+ "default" : "production" ,
1992+ "type" : "string" ,
1993+ },
1994+ "level" : {"enum" : ["low" , "medium" , "high" ], "type" : "string" },
1995+ },
1996+ "required" : ["level" ],
1997+ }
1998+ },
1999+ "required" : ["config" ],
2000+ }
2001+
2002+ model = _build_pydantic_model_from_json_schema ("test_nested" , schema )
2003+
2004+ # Valid data
2005+ instance = model (config = {"type" : "production" , "level" : "high" })
2006+ assert instance .config .type == "production"
2007+ assert instance .config .level == "high"
2008+
2009+ # Invalid const in nested object
2010+ with pytest .raises (ValidationError ):
2011+ model (config = {"type" : "development" , "level" : "low" })
2012+
2013+ # Invalid enum in nested object
2014+ with pytest .raises (ValidationError ):
2015+ model (config = {"type" : "production" , "level" : "critical" })
2016+
2017+
17592018# endregion
0 commit comments