/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2026-2026 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::io::{Cursor, Read}; use kvdb_overlay::{Database, DatabaseOverlay, Error, Result, TreeOverlay, Value}; #[test] fn tree_value_owns_bytes() -> Result<()> { let (db, _folder) = Database::open_temp()?; let value = { let tree = db.open_tree_default("values")?; assert!(tree.get(b"missing")?.is_none()); tree.insert(b"empty", b"")?; assert_eq!(tree.get(b"empty")?.unwrap().as_ref(), b""); tree.insert(b"key", &[42; 128])?; let value = tree.get(b"key")?.unwrap(); assert!(matches!(value, Value::Backend(_))); tree.insert(b"key", b"replacement")?; tree.remove(b"key")?; value }; drop(db); let mut reader = Cursor::new(value); let mut bytes = [0; 128]; reader.read_exact(&mut bytes)?; assert_eq!(bytes, [42; 128]); assert_eq!(reader.read(&mut bytes)?, 0); Ok(()) } #[test] fn overlay_value_borrows_cache() -> Result<()> { let (db, _folder) = Database::open_temp()?; let tree = db.open_tree_default("values")?; tree.insert(b"key", b"backend")?; let mut overlay = TreeOverlay::new(&tree); assert!(overlay.get(b"missing")?.is_none()); let value = overlay.get(b"key")?.unwrap(); assert!(matches!(value, Value::Backend(_))); assert_eq!(value.as_ref(), b"backend"); overlay.insert(b"key", &[7; 128])?; let value = overlay.get(b"key")?.unwrap(); assert!(matches!(value, Value::Cached(_))); assert_eq!( value.as_ref().as_ptr(), overlay.state.cache.get(b"key".as_slice()).unwrap().as_ptr() ); let mut reader = value.as_ref(); let mut bytes = [0; 128]; reader.read_exact(&mut bytes)?; assert_eq!(bytes, [7; 128]); assert_eq!(reader.read(&mut bytes)?, 0); overlay.remove(b"key")?; assert!(overlay.get(b"key")?.is_none()); assert!(tree.get(b"key")?.is_some()); overlay.insert(b"key", b"")?; assert_eq!(overlay.get(b"key")?.unwrap().as_ref(), b""); // Removals take precedence even if a public cache state contains both. overlay.state.removed.insert(b"key".to_vec()); assert!(overlay.get(b"key")?.is_none()); Ok(()) } #[test] fn database_overlay_value() -> Result<()> { let (db, _folder) = Database::open_temp()?; let tree = db.open_tree_default("values")?; tree.insert(b"key", b"backend")?; let mut overlay = DatabaseOverlay::new(&db, vec![])?; assert!(matches!( overlay.get("values", b"key"), Err(Error::CollectionNotFound(_)) )); overlay.open_tree_default("values", false)?; assert!(overlay.get("values", b"missing")?.is_none()); assert_eq!(overlay.get("values", b"key")?.unwrap().as_ref(), b"backend"); overlay.insert("values", b"key", b"cached")?; assert_eq!(overlay.get("values", b"key")?.unwrap().as_ref(), b"cached"); overlay.remove("values", b"key")?; assert!(overlay.get("values", b"key")?.is_none()); overlay.drop_tree("values")?; assert!(matches!( overlay.get("values", b"key"), Err(Error::CollectionNotFound(_)) )); Ok(()) } #[test] fn value_into_vec_copies_bytes() -> Result<()> { for cached in [false, true] { for original in [b"value".as_slice(), b""] { let mut bytes = { let (db, _folder) = Database::open_temp()?; let tree = db.open_tree_default("values")?; tree.insert(b"key", original)?; let mut overlay = TreeOverlay::new(&tree); if cached { overlay.insert(b"key", original)?; } let value = overlay.get(b"key")?.unwrap(); if cached { assert!(matches!(value, Value::Cached(_))); } else { assert!(matches!(value, Value::Backend(_))); } let mut bytes = value.into_vec(); assert_eq!(bytes, original); bytes.reverse(); bytes.push(b'!'); assert_eq!(overlay.get(b"key")?.unwrap().as_ref(), original); assert_eq!(tree.get(b"key")?.unwrap().as_ref(), original); overlay.insert(b"key", b"replacement")?; overlay.remove(b"key")?; bytes }; assert_eq!(bytes.pop(), Some(b'!')); bytes.reverse(); assert_eq!(bytes, original); } } Ok(()) }