update grid4 & heroledger models

This commit is contained in:
Timur Gordon
2025-09-16 14:18:08 +02:00
parent cb1fb0f0ec
commit 53e9a2d4f0
31 changed files with 3216 additions and 399 deletions

View File

@@ -1,7 +1,38 @@
use heromodels_derive::model;
use serde::{Deserialize, Serialize};
// Define the necessary structs and traits for testing
// Make the current crate visible as an extern crate named `heromodels_core`
extern crate self as heromodels_core;
extern crate serde_json; // ensure ::serde_json path resolves
// Mock the heromodels_core API at crate root (visible via the alias above)
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IndexKey {
pub name: &'static str,
pub value: String,
}
pub trait Model: std::fmt::Debug + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + 'static {
fn db_prefix() -> &'static str
where
Self: Sized;
fn get_id(&self) -> u32;
fn base_data_mut(&mut self) -> &mut BaseModelData;
fn db_keys(&self) -> Vec<IndexKey> {
Vec::new()
}
fn indexed_fields() -> Vec<&'static str> {
Vec::new()
}
}
pub trait Index {
type Model: Model;
type Key: ToString + ?Sized;
fn key() -> &'static str;
fn field_name() -> &'static str;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaseModelData {
pub id: u32,
@@ -11,41 +42,18 @@ pub struct BaseModelData {
}
impl BaseModelData {
pub fn new(id: u32) -> Self {
let now = 1000; // Mock timestamp
Self {
id,
created_at: now,
modified_at: now,
comments: Vec::new(),
}
pub fn new() -> Self {
let now = 1000;
Self { id: 0, created_at: now, modified_at: now, comments: Vec::new() }
}
pub fn update_modified(&mut self) { self.modified_at += 1; }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IndexKey {
pub name: &'static str,
pub value: String,
}
pub trait Model: std::fmt::Debug + Clone {
fn db_prefix() -> &'static str;
fn get_id(&self) -> u32;
fn base_data_mut(&mut self) -> &mut BaseModelData;
fn db_keys(&self) -> Vec<IndexKey>;
}
pub trait Index {
type Model: Model;
type Key: ?Sized;
fn key() -> &'static str;
}
// Test struct using the model macro
// Top-level field index tests
#[derive(Debug, Clone, Serialize, Deserialize)]
#[model]
struct TestUser {
base_data: BaseModelData,
pub struct TestUser {
base_data: heromodels_core::BaseModelData,
#[index]
username: String,
@@ -54,25 +62,12 @@ struct TestUser {
is_active: bool,
}
// Test struct with custom index name
#[derive(Debug, Clone, Serialize, Deserialize)]
#[model]
struct TestUserWithCustomIndex {
base_data: BaseModelData,
#[index(name = "custom_username")]
username: String,
#[index]
is_active: bool,
}
#[test]
fn test_basic_model() {
assert_eq!(TestUser::db_prefix(), "test_user");
let user = TestUser {
base_data: BaseModelData::new(1),
base_data: heromodels_core::BaseModelData::new(),
username: "test".to_string(),
is_active: true,
};
@@ -85,22 +80,47 @@ fn test_basic_model() {
assert_eq!(keys[1].value, "true");
}
// Nested path index tests (including vector traversal)
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct GPU { gpu_brand: String }
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct CPU { cpu_brand: String }
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct DeviceInfo { vendor: String, cpu: Vec<CPU>, gpu: Vec<GPU> }
#[derive(Debug, Clone, Serialize, Deserialize)]
#[model]
pub struct NodeLike {
base_data: heromodels_core::BaseModelData,
#[index(path = "vendor")]
#[index(path = "cpu.cpu_brand")]
#[index(path = "gpu.gpu_brand")]
devices: DeviceInfo,
}
#[test]
fn test_custom_index_name() {
let user = TestUserWithCustomIndex {
base_data: BaseModelData::new(1),
username: "test".to_string(),
is_active: true,
fn test_nested_indexes() {
let n = NodeLike {
base_data: heromodels_core::BaseModelData::new(),
devices: DeviceInfo {
vendor: "SuperVendor".to_string(),
cpu: vec![CPU { cpu_brand: "Intel".into() }, CPU { cpu_brand: "AMD".into() }],
gpu: vec![GPU { gpu_brand: "NVIDIA".into() }, GPU { gpu_brand: "AMD".into() }],
},
};
// Check that the Username struct uses the custom index name
assert_eq!(Username::key(), "custom_username");
let mut keys = n.db_keys();
// Sort for deterministic assertions
keys.sort_by(|a,b| a.name.cmp(b.name).then(a.value.cmp(&b.value)));
// Check that the db_keys method returns the correct keys
let keys = user.db_keys();
assert_eq!(keys.len(), 2);
assert_eq!(keys[0].name, "custom_username");
assert_eq!(keys[0].value, "test");
assert_eq!(keys[1].name, "is_active");
assert_eq!(keys[1].value, "true");
// Expect 1 (vendor) + 2 (cpu brands) + 2 (gpu brands) = 5 keys
assert_eq!(keys.len(), 5);
assert!(keys.iter().any(|k| k.name == "vendor" && k.value == "SuperVendor"));
assert!(keys.iter().any(|k| k.name == "cpu.cpu_brand" && k.value == "Intel"));
assert!(keys.iter().any(|k| k.name == "cpu.cpu_brand" && k.value == "AMD"));
assert!(keys.iter().any(|k| k.name == "gpu.gpu_brand" && k.value == "NVIDIA"));
assert!(keys.iter().any(|k| k.name == "gpu.gpu_brand" && k.value == "AMD"));
}