Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions tests/Tests.FSharp/Storage/Merkle.Tests.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module Zeta.Tests.Storage.MerkleTests
#nowarn "0893"

open FsCheck
open FsCheck.FSharp
open FsCheck.Xunit
open FsUnit.Xunit
open global.Xunit
open Zeta.Core
Expand Down Expand Up @@ -34,3 +37,36 @@ let ``MerkleTree LeafDiff detects single-leaf change`` () =
let diff = (MerkleTree leaves2).LeafDiff(MerkleTree leaves1)
diff.Length |> should equal 1
diff.[0] |> should equal 3


// ─── Property: same-leaves determinism + pair collision-freedom ────
// Generalisations of the existing [<Fact>] checks. FsCheck shrinks
// failing inputs to minimal counter-examples — useful if the leaf
// hashing pipeline ever loses determinism (e.g. a regression that
// hashes byte arrays via reference equality) or if tree-building
// introduces accidental collisions on small byte-array inputs.

[<Property>]
let ``MerkleTree root is deterministic for any leaf set`` (leaves: byte array array) =
// Both trees built from the SAME input must produce equal roots.
// Treats null arrays as empty (FsCheck can generate null).
let safe = if isNull leaves then [||] else leaves |> Array.map (fun b -> if isNull b then [||] else b)
let t1 = MerkleTree safe
let t2 = MerkleTree safe
Comment on lines +51 to +55
t1.Root = t2.Root

[<Property>]
let ``MerkleTree root differs under any single-leaf edit (collision-free in practice)``
(NonEmptyArray (leaves: byte array array)) (replacement: byte array) =
// Replace the first leaf, expect a different root. The 128-bit
// XxHash makes practical collisions astronomically unlikely for
// the small inputs FsCheck generates; if the hashing pipeline
// regresses to <128 bits or to a non-collision-resistant hash,
// this property starts failing.
let arr = leaves |> Array.map (fun b -> if isNull b then [||] else b)
let r = if isNull replacement then [||] else replacement
if arr.[0] = r then true // skip cases where replacement is identical
else
let modified = Array.copy arr
modified.[0] <- r
Comment on lines +60 to +71
Comment on lines +68 to +71
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exercise non-zero leaf positions in edit property

This property always edits arr.[0], so it does not actually validate the stated "any single-leaf edit" behavior across leaf positions. A position-dependent bug in tree construction (e.g., incorrect indexing beyond the first leaf) could still pass this test, which weakens the regression coverage the new property is intended to provide.

Useful? React with 👍 / 👎.

(MerkleTree arr).Root <> (MerkleTree modified).Root
Loading