268 lines
9.5 KiB
C#
Executable File
268 lines
9.5 KiB
C#
Executable File
using Xunit;
|
|
using ZB.MOM.WW.CBDD.Core.Collections;
|
|
using ZB.MOM.WW.CBDD.Bson;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using ZB.MOM.WW.CBDD.Core.Indexing;
|
|
using ZB.MOM.WW.CBDD.Core.Storage;
|
|
using System;
|
|
using System.IO;
|
|
using ZB.MOM.WW.CBDD.Shared;
|
|
|
|
namespace ZB.MOM.WW.CBDD.Tests
|
|
{
|
|
public class AdvancedQueryTests : IDisposable
|
|
{
|
|
private readonly string _dbPath;
|
|
private readonly Shared.TestDbContext _db;
|
|
|
|
/// <summary>
|
|
/// Initializes test database state used by advanced query tests.
|
|
/// </summary>
|
|
public AdvancedQueryTests()
|
|
{
|
|
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_advanced_{Guid.NewGuid()}.db");
|
|
_db = new Shared.TestDbContext(_dbPath);
|
|
|
|
// Seed Data
|
|
_db.TestDocuments.Insert(new TestDocument { Category = "A", Amount = 10, Name = "Item1" });
|
|
_db.TestDocuments.Insert(new TestDocument { Category = "A", Amount = 20, Name = "Item2" });
|
|
_db.TestDocuments.Insert(new TestDocument { Category = "B", Amount = 30, Name = "Item3" });
|
|
_db.TestDocuments.Insert(new TestDocument { Category = "B", Amount = 40, Name = "Item4" });
|
|
_db.TestDocuments.Insert(new TestDocument { Category = "C", Amount = 50, Name = "Item5" });
|
|
_db.SaveChanges();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposes test resources and removes temporary files.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
_db.Dispose();
|
|
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies grouping by a simple key returns expected groups and counts.
|
|
/// </summary>
|
|
[Fact]
|
|
public void GroupBy_Simple_Key_Works()
|
|
{
|
|
var groups = _db.TestDocuments.AsQueryable()
|
|
.GroupBy(x => x.Category)
|
|
.ToList();
|
|
|
|
groups.Count.ShouldBe(3);
|
|
|
|
var groupA = groups.First(g => g.Key == "A");
|
|
groupA.Count().ShouldBe(2);
|
|
groupA.ShouldContain(x => x.Amount == 10);
|
|
groupA.ShouldContain(x => x.Amount == 20);
|
|
|
|
var groupB = groups.First(g => g.Key == "B");
|
|
groupB.Count().ShouldBe(2);
|
|
|
|
var groupC = groups.First(g => g.Key == "C");
|
|
groupC.Count().ShouldBe(1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies grouped projection with aggregation returns expected totals.
|
|
/// </summary>
|
|
[Fact]
|
|
public void GroupBy_With_Aggregation_Select()
|
|
{
|
|
var results = _db.TestDocuments.AsQueryable()
|
|
.GroupBy(x => x.Category)
|
|
.Select(g => new { Category = g.Key, Total = g.Sum(x => x.Amount) })
|
|
.OrderBy(x => x.Category)
|
|
.ToList();
|
|
|
|
results.Count.ShouldBe(3);
|
|
results[0].Category.ShouldBe("A");
|
|
results[0].Total.ShouldBe(30); // 10 + 20
|
|
|
|
results[1].Category.ShouldBe("B");
|
|
results[1].Total.ShouldBe(70); // 30 + 40
|
|
|
|
results[2].Category.ShouldBe("C");
|
|
results[2].Total.ShouldBe(50); // 50
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies direct aggregate operators return expected values.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Aggregations_Direct_Works()
|
|
{
|
|
var query = _db.TestDocuments.AsQueryable();
|
|
|
|
query.Count().ShouldBe(5);
|
|
query.Sum(x => x.Amount).ShouldBe(150);
|
|
query.Average(x => x.Amount).ShouldBe(30.0);
|
|
query.Min(x => x.Amount).ShouldBe(10);
|
|
query.Max(x => x.Amount).ShouldBe(50);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies aggregate operators with predicates return expected values.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Aggregations_With_Predicate_Works()
|
|
{
|
|
var query = _db.TestDocuments.AsQueryable().Where(x => x.Category == "A");
|
|
|
|
query.Count().ShouldBe(2);
|
|
query.Sum(x => x.Amount).ShouldBe(30);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies in-memory join query execution returns expected rows.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Join_Works_InMemory()
|
|
{
|
|
// Create a second collection for joining
|
|
_db.OrderDocuments.Insert(new OrderDocument { ItemName = "Item1", Quantity = 5 });
|
|
_db.OrderDocuments.Insert(new OrderDocument { ItemName = "Item3", Quantity = 2 });
|
|
_db.SaveChanges();
|
|
|
|
var query = _db.TestDocuments.AsQueryable()
|
|
.Join(_db.OrderDocuments.AsQueryable(),
|
|
doc => doc.Name,
|
|
order => order.ItemName,
|
|
(doc, order) => new { doc.Name, doc.Category, order.Quantity })
|
|
.OrderBy(x => x.Name)
|
|
.ToList();
|
|
|
|
query.Count.ShouldBe(2);
|
|
|
|
query[0].Name.ShouldBe("Item1");
|
|
query[0].Category.ShouldBe("A");
|
|
query[0].Quantity.ShouldBe(5);
|
|
|
|
query[1].Name.ShouldBe("Item3");
|
|
query[1].Category.ShouldBe("B");
|
|
query[1].Quantity.ShouldBe(2);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Verifies projection of nested object properties works.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Select_Project_Nested_Object()
|
|
{
|
|
var doc = new ComplexDocument
|
|
{
|
|
Id = ObjectId.NewObjectId(),
|
|
Title = "Order1",
|
|
ShippingAddress = new Address { City = new City { Name = "New York" }, Street = "5th Ave" },
|
|
Items = new List<OrderItem>
|
|
{
|
|
new OrderItem { Name = "Laptop", Price = 1000 },
|
|
new OrderItem { Name = "Mouse", Price = 50 }
|
|
}
|
|
};
|
|
_db.ComplexDocuments.Insert(doc);
|
|
_db.SaveChanges();
|
|
|
|
var query = _db.ComplexDocuments.AsQueryable()
|
|
.Select(x => x.ShippingAddress)
|
|
.ToList();
|
|
|
|
query.Count().ShouldBe(1);
|
|
query[0].City.Name.ShouldBe("New York");
|
|
query[0].Street.ShouldBe("5th Ave");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies projection of nested scalar fields works.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Select_Project_Nested_Field()
|
|
{
|
|
var doc = new ComplexDocument
|
|
{
|
|
Id = ObjectId.NewObjectId(),
|
|
Title = "Order1",
|
|
ShippingAddress = new Address { City = new City { Name = "New York" }, Street = "5th Ave" }
|
|
};
|
|
_db.ComplexDocuments.Insert(doc);
|
|
_db.SaveChanges();
|
|
|
|
var cities = _db.ComplexDocuments.AsQueryable()
|
|
.Select(x => x.ShippingAddress.City.Name)
|
|
.ToList();
|
|
|
|
cities.Count().ShouldBe(1);
|
|
cities[0].ShouldBe("New York");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies anonymous projection including nested values works.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Select_Anonymous_Complex()
|
|
{
|
|
ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers.ZB_MOM_WW_CBDD_Shared_CityMapper cityMapper = new ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers.ZB_MOM_WW_CBDD_Shared_CityMapper();
|
|
var doc = new ComplexDocument
|
|
{
|
|
Id = ObjectId.NewObjectId(),
|
|
Title = "Order1",
|
|
ShippingAddress = new Address { City = new City { Name = "New York" }, Street = "5th Ave" }
|
|
};
|
|
|
|
|
|
_db.ComplexDocuments.Insert(doc);
|
|
_db.SaveChanges();
|
|
|
|
var result = _db.ComplexDocuments.AsQueryable()
|
|
.Select(x => new { x.Title, x.ShippingAddress.City })
|
|
.ToList();
|
|
|
|
result.Count().ShouldBe(1);
|
|
result[0].Title.ShouldBe("Order1");
|
|
result[0].City.Name.ShouldBe("New York");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies projection and retrieval of nested arrays of objects works.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Select_Project_Nested_Array_Of_Objects()
|
|
{
|
|
var doc = new ComplexDocument
|
|
{
|
|
Id = ObjectId.NewObjectId(),
|
|
Title = "Order with Items",
|
|
ShippingAddress = new Address { City = new City { Name = "Los Angeles" }, Street = "Hollywood Blvd" },
|
|
Items = new List<OrderItem>
|
|
{
|
|
new OrderItem { Name = "Laptop", Price = 1500 },
|
|
new OrderItem { Name = "Mouse", Price = 25 },
|
|
new OrderItem { Name = "Keyboard", Price = 75 }
|
|
}
|
|
};
|
|
_db.ComplexDocuments.Insert(doc);
|
|
_db.SaveChanges();
|
|
|
|
// Retrieve the full document and verify Items array
|
|
var retrieved = _db.ComplexDocuments.FindAll().First();
|
|
|
|
retrieved.Title.ShouldBe("Order with Items");
|
|
retrieved.ShippingAddress.City.Name.ShouldBe("Los Angeles");
|
|
retrieved.ShippingAddress.Street.ShouldBe("Hollywood Blvd");
|
|
|
|
// Verify array of nested objects
|
|
retrieved.Items.Count.ShouldBe(3);
|
|
retrieved.Items[0].Name.ShouldBe("Laptop");
|
|
retrieved.Items[0].Price.ShouldBe(1500);
|
|
retrieved.Items[1].Name.ShouldBe("Mouse");
|
|
retrieved.Items[1].Price.ShouldBe(25);
|
|
retrieved.Items[2].Name.ShouldBe("Keyboard");
|
|
retrieved.Items[2].Price.ShouldBe(75);
|
|
}
|
|
}
|
|
}
|