Skip to content

Commit 5ee5ac0

Browse files
authored
Adding more tests for JSON queries with weird/malformed JSON: (#35597)
- duplicate properties (navigtions and scalars) - JSON property names being empty strings - JSON property names being null (invalid)
1 parent 5c2afbb commit 5ee5ac0

File tree

3 files changed

+494
-1
lines changed

3 files changed

+494
-1
lines changed

test/EFCore.Relational.Specification.Tests/Query/AdHocJsonQueryTestBase.cs

+290
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,296 @@ protected virtual void BuildModelNotICollection(ModelBuilder modelBuilder)
14671467

14681468
#endregion
14691469

1470+
#region BadJsonProperties
1471+
1472+
[ConditionalFact]
1473+
public virtual async Task Bad_json_properties_duplicated_navigations_tracking()
1474+
{
1475+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1476+
onModelCreating: BuildModelBadJsonProperties,
1477+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1478+
seed: SeedBadJsonProperties);
1479+
1480+
using (var context = contextFactory.CreateContext())
1481+
{
1482+
var baseline = await context.Entities.SingleAsync(x => x.Scenario == "baseline");
1483+
var dupNavs = await context.Entities.SingleAsync(x => x.Scenario == "duplicated navigations");
1484+
1485+
// for tracking, first one wins
1486+
Assert.Equal(baseline.RequiredReference.NestedOptional.Text, dupNavs.RequiredReference.NestedOptional.Text);
1487+
Assert.Equal(baseline.RequiredReference.NestedRequired.Text, dupNavs.RequiredReference.NestedRequired.Text);
1488+
Assert.Equal(baseline.RequiredReference.NestedCollection[0].Text, dupNavs.RequiredReference.NestedCollection[0].Text);
1489+
Assert.Equal(baseline.RequiredReference.NestedCollection[1].Text, dupNavs.RequiredReference.NestedCollection[1].Text);
1490+
1491+
Assert.Equal(baseline.OptionalReference.NestedOptional.Text, dupNavs.OptionalReference.NestedOptional.Text);
1492+
Assert.Equal(baseline.OptionalReference.NestedRequired.Text, dupNavs.OptionalReference.NestedRequired.Text);
1493+
Assert.Equal(baseline.OptionalReference.NestedCollection[0].Text, dupNavs.OptionalReference.NestedCollection[0].Text);
1494+
Assert.Equal(baseline.OptionalReference.NestedCollection[1].Text, dupNavs.OptionalReference.NestedCollection[1].Text);
1495+
1496+
Assert.Equal(baseline.Collection[0].NestedOptional.Text, dupNavs.Collection[0].NestedOptional.Text);
1497+
Assert.Equal(baseline.Collection[0].NestedRequired.Text, dupNavs.Collection[0].NestedRequired.Text);
1498+
Assert.Equal(baseline.Collection[0].NestedCollection[0].Text, dupNavs.Collection[0].NestedCollection[0].Text);
1499+
Assert.Equal(baseline.Collection[0].NestedCollection[1].Text, dupNavs.Collection[0].NestedCollection[1].Text);
1500+
1501+
Assert.Equal(baseline.Collection[1].NestedOptional.Text, dupNavs.Collection[1].NestedOptional.Text);
1502+
Assert.Equal(baseline.Collection[1].NestedRequired.Text, dupNavs.Collection[1].NestedRequired.Text);
1503+
Assert.Equal(baseline.Collection[1].NestedCollection[0].Text, dupNavs.Collection[1].NestedCollection[0].Text);
1504+
Assert.Equal(baseline.Collection[1].NestedCollection[1].Text, dupNavs.Collection[1].NestedCollection[1].Text);
1505+
}
1506+
}
1507+
1508+
[ConditionalFact]
1509+
public virtual async Task Bad_json_properties_duplicated_navigations_no_tracking()
1510+
{
1511+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1512+
onModelCreating: BuildModelBadJsonProperties,
1513+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1514+
seed: SeedBadJsonProperties);
1515+
1516+
using (var context = contextFactory.CreateContext())
1517+
{
1518+
var query = context.Entities.AsNoTracking();
1519+
1520+
var baseline = query.Single(x => x.Scenario == "baseline");
1521+
var dupNavs = query.Single(x => x.Scenario == "duplicated navigations");
1522+
1523+
// for no tracking, last one wins
1524+
Assert.Equal(baseline.RequiredReference.NestedOptional.Text + " dupnav", dupNavs.RequiredReference.NestedOptional.Text);
1525+
Assert.Equal(baseline.RequiredReference.NestedRequired.Text + " dupnav", dupNavs.RequiredReference.NestedRequired.Text);
1526+
Assert.Equal(baseline.RequiredReference.NestedCollection[0].Text + " dupnav", dupNavs.RequiredReference.NestedCollection[0].Text);
1527+
Assert.Equal(baseline.RequiredReference.NestedCollection[1].Text + " dupnav", dupNavs.RequiredReference.NestedCollection[1].Text);
1528+
1529+
Assert.Equal(baseline.OptionalReference.NestedOptional.Text + " dupnav", dupNavs.OptionalReference.NestedOptional.Text);
1530+
Assert.Equal(baseline.OptionalReference.NestedRequired.Text + " dupnav", dupNavs.OptionalReference.NestedRequired.Text);
1531+
Assert.Equal(baseline.OptionalReference.NestedCollection[0].Text + " dupnav", dupNavs.OptionalReference.NestedCollection[0].Text);
1532+
Assert.Equal(baseline.OptionalReference.NestedCollection[1].Text + " dupnav", dupNavs.OptionalReference.NestedCollection[1].Text);
1533+
1534+
Assert.Equal(baseline.Collection[0].NestedOptional.Text + " dupnav", dupNavs.Collection[0].NestedOptional.Text);
1535+
Assert.Equal(baseline.Collection[0].NestedRequired.Text + " dupnav", dupNavs.Collection[0].NestedRequired.Text);
1536+
Assert.Equal(baseline.Collection[0].NestedCollection[0].Text + " dupnav", dupNavs.Collection[0].NestedCollection[0].Text);
1537+
Assert.Equal(baseline.Collection[0].NestedCollection[1].Text + " dupnav", dupNavs.Collection[0].NestedCollection[1].Text);
1538+
1539+
Assert.Equal(baseline.Collection[1].NestedOptional.Text + " dupnav", dupNavs.Collection[1].NestedOptional.Text);
1540+
Assert.Equal(baseline.Collection[1].NestedRequired.Text + " dupnav", dupNavs.Collection[1].NestedRequired.Text);
1541+
Assert.Equal(baseline.Collection[1].NestedCollection[0].Text + " dupnav", dupNavs.Collection[1].NestedCollection[0].Text);
1542+
Assert.Equal(baseline.Collection[1].NestedCollection[1].Text + " dupnav", dupNavs.Collection[1].NestedCollection[1].Text);
1543+
}
1544+
}
1545+
1546+
[ConditionalTheory]
1547+
[InlineData(true)]
1548+
[InlineData(false)]
1549+
public virtual async Task Bad_json_properties_duplicated_scalars(bool noTracking)
1550+
{
1551+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1552+
onModelCreating: BuildModelBadJsonProperties,
1553+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1554+
seed: SeedBadJsonProperties);
1555+
1556+
using (var context = contextFactory.CreateContext())
1557+
{
1558+
var query = noTracking ? context.Entities.AsNoTracking() : context.Entities;
1559+
1560+
var baseline = await query.SingleAsync(x => x.Scenario == "baseline");
1561+
var dupProps = await query.SingleAsync(x => x.Scenario == "duplicated scalars");
1562+
1563+
Assert.Equal(baseline.RequiredReference.NestedOptional.Text + " dupprop", dupProps.RequiredReference.NestedOptional.Text);
1564+
Assert.Equal(baseline.RequiredReference.NestedRequired.Text + " dupprop", dupProps.RequiredReference.NestedRequired.Text);
1565+
Assert.Equal(baseline.RequiredReference.NestedCollection[0].Text + " dupprop", dupProps.RequiredReference.NestedCollection[0].Text);
1566+
Assert.Equal(baseline.RequiredReference.NestedCollection[1].Text + " dupprop", dupProps.RequiredReference.NestedCollection[1].Text);
1567+
1568+
Assert.Equal(baseline.OptionalReference.NestedOptional.Text + " dupprop", dupProps.OptionalReference.NestedOptional.Text);
1569+
Assert.Equal(baseline.OptionalReference.NestedRequired.Text + " dupprop", dupProps.OptionalReference.NestedRequired.Text);
1570+
Assert.Equal(baseline.OptionalReference.NestedCollection[0].Text + " dupprop", dupProps.OptionalReference.NestedCollection[0].Text);
1571+
Assert.Equal(baseline.OptionalReference.NestedCollection[1].Text + " dupprop", dupProps.OptionalReference.NestedCollection[1].Text);
1572+
1573+
Assert.Equal(baseline.Collection[0].NestedOptional.Text + " dupprop", dupProps.Collection[0].NestedOptional.Text);
1574+
Assert.Equal(baseline.Collection[0].NestedRequired.Text + " dupprop", dupProps.Collection[0].NestedRequired.Text);
1575+
Assert.Equal(baseline.Collection[0].NestedCollection[0].Text + " dupprop", dupProps.Collection[0].NestedCollection[0].Text);
1576+
Assert.Equal(baseline.Collection[0].NestedCollection[1].Text + " dupprop", dupProps.Collection[0].NestedCollection[1].Text);
1577+
1578+
Assert.Equal(baseline.Collection[1].NestedOptional.Text + " dupprop", dupProps.Collection[1].NestedOptional.Text);
1579+
Assert.Equal(baseline.Collection[1].NestedRequired.Text + " dupprop", dupProps.Collection[1].NestedRequired.Text);
1580+
Assert.Equal(baseline.Collection[1].NestedCollection[0].Text + " dupprop", dupProps.Collection[1].NestedCollection[0].Text);
1581+
Assert.Equal(baseline.Collection[1].NestedCollection[1].Text + " dupprop", dupProps.Collection[1].NestedCollection[1].Text);
1582+
}
1583+
}
1584+
1585+
[ConditionalTheory]
1586+
[InlineData(true)]
1587+
[InlineData(false)]
1588+
public virtual async Task Bad_json_properties_empty_navigations(bool noTracking)
1589+
{
1590+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1591+
onModelCreating: BuildModelBadJsonProperties,
1592+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1593+
seed: SeedBadJsonProperties);
1594+
1595+
using (var context = contextFactory.CreateContext())
1596+
{
1597+
var query = noTracking ? context.Entities.AsNoTracking() : context.Entities;
1598+
var emptyNavs = await query.SingleAsync(x => x.Scenario == "empty navigation property names");
1599+
1600+
Assert.Null(emptyNavs.RequiredReference.NestedOptional);
1601+
Assert.Null(emptyNavs.RequiredReference.NestedRequired);
1602+
Assert.Null(emptyNavs.RequiredReference.NestedCollection);
1603+
1604+
Assert.Null(emptyNavs.OptionalReference.NestedOptional);
1605+
Assert.Null(emptyNavs.OptionalReference.NestedRequired);
1606+
Assert.Null(emptyNavs.OptionalReference.NestedCollection);
1607+
1608+
Assert.Null(emptyNavs.Collection[0].NestedOptional);
1609+
Assert.Null(emptyNavs.Collection[0].NestedRequired);
1610+
Assert.Null(emptyNavs.Collection[0].NestedCollection);
1611+
1612+
Assert.Null(emptyNavs.Collection[1].NestedOptional);
1613+
Assert.Null(emptyNavs.Collection[1].NestedRequired);
1614+
Assert.Null(emptyNavs.Collection[1].NestedCollection);
1615+
}
1616+
}
1617+
1618+
[ConditionalTheory]
1619+
[InlineData(true)]
1620+
[InlineData(false)]
1621+
public virtual async Task Bad_json_properties_empty_scalars(bool noTracking)
1622+
{
1623+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1624+
onModelCreating: BuildModelBadJsonProperties,
1625+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1626+
seed: SeedBadJsonProperties);
1627+
1628+
using (var context = contextFactory.CreateContext())
1629+
{
1630+
var query = noTracking ? context.Entities.AsNoTracking() : context.Entities;
1631+
var emptyNavs = await query.SingleAsync(x => x.Scenario == "empty scalar property names");
1632+
1633+
Assert.Null(emptyNavs.RequiredReference.NestedOptional.Text);
1634+
Assert.Null(emptyNavs.RequiredReference.NestedRequired.Text);
1635+
Assert.Null(emptyNavs.RequiredReference.NestedCollection[0].Text);
1636+
Assert.Null(emptyNavs.RequiredReference.NestedCollection[1].Text);
1637+
1638+
Assert.Null(emptyNavs.OptionalReference.NestedOptional.Text);
1639+
Assert.Null(emptyNavs.OptionalReference.NestedRequired.Text);
1640+
Assert.Null(emptyNavs.OptionalReference.NestedCollection[0].Text);
1641+
Assert.Null(emptyNavs.OptionalReference.NestedCollection[1].Text);
1642+
1643+
Assert.Null(emptyNavs.Collection[0].NestedOptional.Text);
1644+
Assert.Null(emptyNavs.Collection[0].NestedRequired.Text);
1645+
Assert.Null(emptyNavs.Collection[0].NestedCollection[0].Text);
1646+
Assert.Null(emptyNavs.Collection[0].NestedCollection[1].Text);
1647+
1648+
Assert.Null(emptyNavs.Collection[1].NestedOptional.Text);
1649+
Assert.Null(emptyNavs.Collection[1].NestedRequired.Text);
1650+
Assert.Null(emptyNavs.Collection[1].NestedCollection[0].Text);
1651+
Assert.Null(emptyNavs.Collection[1].NestedCollection[1].Text);
1652+
}
1653+
}
1654+
1655+
[ConditionalTheory]
1656+
[InlineData(true)]
1657+
[InlineData(false)]
1658+
public virtual async Task Bad_json_properties_null_navigations(bool noTracking)
1659+
{
1660+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1661+
onModelCreating: BuildModelBadJsonProperties,
1662+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1663+
seed: SeedBadJsonProperties);
1664+
1665+
using (var context = contextFactory.CreateContext())
1666+
{
1667+
var query = noTracking ? context.Entities.AsNoTracking() : context.Entities;
1668+
1669+
await Assert.ThrowsAnyAsync<JsonException>(
1670+
() => query.SingleAsync(x => x.Scenario == "null navigation property names"));
1671+
}
1672+
}
1673+
1674+
[ConditionalTheory]
1675+
[InlineData(true)]
1676+
[InlineData(false)]
1677+
public virtual async Task Bad_json_properties_null_scalars(bool noTracking)
1678+
{
1679+
var contextFactory = await InitializeAsync<ContextBadJsonProperties>(
1680+
onModelCreating: BuildModelBadJsonProperties,
1681+
onConfiguring: b => b.ConfigureWarnings(ConfigureWarnings),
1682+
seed: SeedBadJsonProperties);
1683+
1684+
using (var context = contextFactory.CreateContext())
1685+
{
1686+
var query = noTracking ? context.Entities.AsNoTracking() : context.Entities;
1687+
1688+
var message = (await Assert.ThrowsAnyAsync<JsonException>(
1689+
() => query.SingleAsync(x => x.Scenario == "null scalar property names"))).Message;
1690+
1691+
Assert.StartsWith("'n' is an invalid start of a property name. Expected a '\"'.", message);
1692+
}
1693+
}
1694+
1695+
protected class ContextBadJsonProperties(DbContextOptions options) : DbContext(options)
1696+
{
1697+
public DbSet<Entity> Entities { get; set; }
1698+
1699+
public class Entity
1700+
{
1701+
public int Id { get; set; }
1702+
public string Scenario { get; set; }
1703+
public JsonRoot OptionalReference { get; set; }
1704+
public JsonRoot RequiredReference { get; set; }
1705+
public List<JsonRoot> Collection { get; set; }
1706+
}
1707+
1708+
public class JsonRoot
1709+
{
1710+
public JsonBranch NestedRequired { get; set; }
1711+
public JsonBranch NestedOptional { get; set; }
1712+
public List<JsonBranch> NestedCollection { get; set; }
1713+
}
1714+
1715+
public class JsonBranch
1716+
{
1717+
public string Text { get; set; }
1718+
}
1719+
}
1720+
1721+
protected abstract Task SeedBadJsonProperties(ContextBadJsonProperties ctx);
1722+
1723+
protected virtual void BuildModelBadJsonProperties(ModelBuilder modelBuilder)
1724+
=> modelBuilder.Entity<ContextBadJsonProperties.Entity>(
1725+
b =>
1726+
{
1727+
b.ToTable("Entities");
1728+
b.Property(x => x.Id).ValueGeneratedNever();
1729+
1730+
b.OwnsOne(
1731+
x => x.RequiredReference, b =>
1732+
{
1733+
b.ToJson().HasColumnType(JsonColumnType);
1734+
b.OwnsOne(x => x.NestedOptional);
1735+
b.OwnsOne(x => x.NestedRequired);
1736+
b.OwnsMany(x => x.NestedCollection);
1737+
});
1738+
1739+
b.OwnsOne(
1740+
x => x.OptionalReference, b =>
1741+
{
1742+
b.ToJson().HasColumnType(JsonColumnType);
1743+
b.OwnsOne(x => x.NestedOptional);
1744+
b.OwnsOne(x => x.NestedRequired);
1745+
b.OwnsMany(x => x.NestedCollection);
1746+
});
1747+
1748+
b.OwnsMany(
1749+
x => x.Collection, b =>
1750+
{
1751+
b.ToJson().HasColumnType(JsonColumnType);
1752+
b.OwnsOne(x => x.NestedOptional);
1753+
b.OwnsOne(x => x.NestedRequired);
1754+
b.OwnsMany(x => x.NestedCollection);
1755+
});
1756+
});
1757+
1758+
#endregion
1759+
14701760
protected TestSqlLoggerFactory TestSqlLoggerFactory
14711761
=> (TestSqlLoggerFactory)ListLoggerFactory;
14721762

0 commit comments

Comments
 (0)