Full Arboricx parsing in tricu
This commit is contained in:
553
test/Spec.hs
553
test/Spec.hs
@@ -49,6 +49,7 @@ tests = testGroup "Tricu Tests"
|
||||
, wireTests
|
||||
, byteListUtilities
|
||||
, binaryReaderTests
|
||||
, manifestReadingTests
|
||||
]
|
||||
|
||||
lexer :: TestTree
|
||||
@@ -2182,3 +2183,555 @@ binaryReaderTests = testGroup "Binary Reader Tests"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= Fork Leaf Leaf
|
||||
]
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Manifest reading tests (Steps 1-9)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
-- Build a minimal manifest:
|
||||
-- magic "ARBMNFST" (8) + version 1.0 (4) +
|
||||
-- schema "arboricx.bundle.manifest.v1" (4+27=31) +
|
||||
-- bundleType "tree-calculus-executable-object" (4+31=35) +
|
||||
-- treeCalculus "tree-calculus.v1" (4+16=20) +
|
||||
-- treeHashAlgorithm "sha256" (4+6=10) +
|
||||
-- treeHashDomain "arboricx.merkle.node.v1" (4+23=27) +
|
||||
-- treeNodePayload "arboricx.merkle.payload.v1" (4+26=30) +
|
||||
-- runtimeSemantics "tree-calculus.v1" (4+16=20) +
|
||||
-- runtimeEvaluation "normal-order" (4+12=16) +
|
||||
-- runtimeAbi "arboricx.abi.tree.v1" (4+20=24) +
|
||||
-- capabilityCount 0 (4) +
|
||||
-- closure 0 (1) +
|
||||
-- rootCount 1 (4) +
|
||||
-- root: hash (32) + role "default" (4+7=11) = 43 +
|
||||
-- exportCount 1 (4) +
|
||||
-- export: name "term" (4+4=8) + root (32) + kind "term" (4+4=8) + abi "arboricx.abi.tree.v1" (4+20=24) = 72 +
|
||||
-- Total core = 8+4+31+35+20+10+27+30+20+16+24+4+1+4+43+4+72 = 378 bytes
|
||||
|
||||
minimalManifestCoreBytes :: [Integer]
|
||||
minimalManifestCoreBytes = [65,82,66,77,78,70,83,84] -- ARBMNFST magic
|
||||
++ u16 1 ++ u16 0 -- version 1.0
|
||||
++ lengthPrefixed "arboricx.bundle.manifest.v1" -- schema
|
||||
++ lengthPrefixed "tree-calculus-executable-object" -- bundleType
|
||||
++ lengthPrefixed "tree-calculus.v1" -- treeCalculus
|
||||
++ lengthPrefixed "sha256" -- treeHashAlgorithm
|
||||
++ lengthPrefixed "arboricx.merkle.node.v1" -- treeHashDomain
|
||||
++ lengthPrefixed "arboricx.merkle.payload.v1" -- treeNodePayload
|
||||
++ lengthPrefixed "tree-calculus.v1" -- runtimeSemantics
|
||||
++ lengthPrefixed "normal-order" -- runtimeEvaluation
|
||||
++ lengthPrefixed "arboricx.abi.tree.v1" -- runtimeAbi
|
||||
++ u32 0 -- 0 capabilities
|
||||
++ [0] -- closure complete
|
||||
++ u32 1 -- 1 root
|
||||
++ replicate 32 0 -- placeholder root hash
|
||||
++ lengthPrefixed "default" -- root role
|
||||
++ u32 1 -- 1 export
|
||||
++ lengthPrefixed "term" -- export name
|
||||
++ replicate 32 0 -- placeholder export root hash
|
||||
++ lengthPrefixed "term" -- export kind
|
||||
++ lengthPrefixed "arboricx.abi.tree.v1" -- export abi
|
||||
|
||||
lengthPrefixed :: String -> [Integer]
|
||||
lengthPrefixed s = u32 (fromIntegral (length s)) ++ map (fromIntegral . fromEnum) s
|
||||
|
||||
-- Full manifest: core + 0 metadata + 0 extension = core + u32(0) + u32(0)
|
||||
fullMinimalManifestBytes :: [Integer]
|
||||
fullMinimalManifestBytes = minimalManifestCoreBytes ++ u32 0 ++ u32 0
|
||||
|
||||
-- Create TLV list with two entries:
|
||||
-- tag 1 (package), value "my-pkg", then tag 2 (version), value "1.0"
|
||||
-- then "rest" bytes
|
||||
|
||||
tlvForTagAndValue :: Integer -> String -> [Integer]
|
||||
tlvForTagAndValue tag val =
|
||||
u16 (fromIntegral tag) ++ lengthPrefixed val
|
||||
|
||||
-- Build a pair of (tag, value) TLV
|
||||
makeTLVPair :: Integer -> String -> String
|
||||
makeTLVPair tag val =
|
||||
"[(pair " ++ bytesExpr [0, fromIntegral tag] ++ " "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) val) ++ ")]"
|
||||
|
||||
exportEntryExpr :: String -> [Integer] -> String -> String -> String
|
||||
exportEntryExpr name rootHashBytes kind abi =
|
||||
"(pair " ++ bytesExpr (map (fromIntegral . fromEnum) name) ++ " "
|
||||
++ "(pair " ++ bytesExpr rootHashBytes ++ " "
|
||||
++ "(pair " ++ bytesExpr (map (fromIntegral . fromEnum) kind) ++ " "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) abi) ++ ")))"
|
||||
|
||||
-- Build list of export entries for the test
|
||||
singleExportExpr :: String
|
||||
singleExportExpr =
|
||||
"[" ++ exportEntryExpr "main" (replicate 32 0) "term" "arboricx.abi.tree.v1" ++ "]"
|
||||
|
||||
multiExportExpr :: String
|
||||
multiExportExpr =
|
||||
"["
|
||||
++ exportEntryExpr "main" (replicate 32 0) "term" "arboricx.abi.tree.v1"
|
||||
-- ++ ", "
|
||||
++ exportEntryExpr "test" (replicate 32 1) "term" "arboricx.abi.tree.v1"
|
||||
++ "]"
|
||||
|
||||
-- Helper to build a minimal valid manifest core
|
||||
-- Returns a tricu expression representing the parsed core structure
|
||||
buildValidCoreExpr :: String
|
||||
buildValidCoreExpr =
|
||||
"(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "arboricx.bundle.manifest.v1") ++ " " -- schema
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "tree-calculus-executable-object") ++ " " -- bundleType
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "tree-calculus.v1") ++ " " -- treeCalculus
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "sha256") ++ " " -- treeHashAlgorithm
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "arboricx.merkle.node.v1") ++ " " -- treeHashDomain
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "arboricx.merkle.payload.v1") ++ " " -- treeNodePayload
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "tree-calculus.v1") ++ " " -- runtimeSemantics
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "normal-order") ++ " " -- runtimeEvaluation
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "arboricx.abi.tree.v1") ++ " " -- runtimeAbi
|
||||
++ "(pair "
|
||||
++ "[] " -- capabilities
|
||||
++ "(pair "
|
||||
++ "0 " -- closure
|
||||
++ "(pair "
|
||||
++ "[(pair " ++ bytesExpr (replicate 32 0) ++ " "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "default") ++ ")" -- roots (1 root)
|
||||
++ "] "
|
||||
++ "[(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "term") ++ " "
|
||||
++ "(pair " ++ bytesExpr (replicate 32 0) ++ " "
|
||||
++ "(pair "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "term") ++ " "
|
||||
++ bytesExpr (map (fromIntegral . fromEnum) "arboricx.abi.tree.v1") ++ ")))" -- exports (1 export)
|
||||
++ "])"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ "]"
|
||||
++ ")"
|
||||
|
||||
-- Build a tricu expression that extracts a specific manifest field from
|
||||
-- readArboricxBundle result and returns it as a byte-list T value.
|
||||
-- The Haskell test then uses toString to convert it to a String.
|
||||
extractManifestField :: ByteString -> String -> String
|
||||
extractManifestField fixtureBytes fieldName =
|
||||
"matchResult "
|
||||
++ " (errCode rest : errCode) "
|
||||
++ " (bundleResult rest : "
|
||||
++ " matchPair "
|
||||
++ " (validCore metadataWithExtensions : "
|
||||
++ " " ++ fieldName ++ " validCore) "
|
||||
++ " bundleResult) "
|
||||
++ " (readArboricxBundle " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")"
|
||||
|
||||
manifestReadingTests :: TestTree
|
||||
manifestReadingTests = testGroup "Manifest Reading Tests"
|
||||
[
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 1: readManifestMagic
|
||||
-- ------------------------------------------------------------------------
|
||||
testCase "readManifestMagic: accepts correct manifest magic and preserves rest" $ do
|
||||
let input = "readManifestMagic ((append arboricxManifestMagic) [(1) (2)])"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= okT unitT (bytesT [1,2])
|
||||
|
||||
, testCase "readManifestMagic: rejects wrong magic" $ do
|
||||
let input = "readManifestMagic [(65) (83) (66) (77) (78) (70) (83) (84)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= errT unexpectedBytesT (bytesT [65,83,66,77,78,70,83,84])
|
||||
|
||||
, testCase "readManifestMagic: short input returns EOF" $ do
|
||||
let input = "readManifestMagic [(65) (82) (66) (77)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= errT eofT (bytesT [65,82,66,77])
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 2: readLengthPrefixedString
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "readLengthPrefixedString: reads a 5-byte string" $ do
|
||||
let input = "readLengthPrefixedString [(0) (0) (0) (5) (104) (101) (108) (108) (111) (99) (111) (110) (116) (101) (114)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= okT (bytesT [104,101,108,108,111]) (bytesT [99,111,110,116,101,114])
|
||||
|
||||
, testCase "readLengthPrefixedString: reads an empty string" $ do
|
||||
let input = "readLengthPrefixedString [(0) (0) (0) (0) (97) (98)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= okT (bytesT []) (bytesT [97,98])
|
||||
|
||||
, testCase "readLengthPrefixedString: short payload returns EOF" $ do
|
||||
let input = "readLengthPrefixedString [(0) (0) (0) (5) (104) (101) (108)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= errT eofT (bytesT [104,101,108])
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 3: readManifestCore (construct a minimal valid manifest)
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "readManifestCore: reads a minimal valid manifest core" $ do
|
||||
let input = "readManifestCore " ++ bytesExpr minimalManifestCoreBytes
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork Leaf Leaf) -> assertFailure "should be ok, not t"
|
||||
(Fork _ (Fork _ rest)) -> return () -- ok case: pair true (pair value rest)
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
, testCase "readManifestCore: returns error on wrong magic" $ do
|
||||
let badMagic = [65,83,66,77,78,70,83,84] ++ (drop 8 minimalManifestCoreBytes)
|
||||
let input = "readManifestCore " ++ bytesExpr badMagic
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork falseT _) -> return () -- err case: pair false (pair code rest)
|
||||
_ -> assertFailure $ "expected err result, got: " ++ show actualResult
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 4: TLV reader
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "readTLV: reads a metadata TLV entry" $ do
|
||||
-- tag = u16 1 = [(0)(1)], length = u32 3 = [(0)(0)(0)(3)], value = "foo" = [102,111,111]
|
||||
let input = "readTLV [(0) (1) (0) (0) (0) (3) (102) (111) (111) (99) (111) (110) (116) (114) (101) (115) (116)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ rest)) -> do
|
||||
-- ok case: verify the value pair
|
||||
let value = case result env of
|
||||
(Fork _ (Fork val _)) -> case val of
|
||||
(Fork tagVal _) -> tagVal
|
||||
_ -> Leaf
|
||||
return ()
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
, testCase "readTLV: returns EOF on empty input" $ do
|
||||
let input = "readTLV []"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= errT eofT (bytesT [])
|
||||
|
||||
, testCase "readTLV: returns EOF on short tag" $ do
|
||||
let input = "readTLV [(0)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= errT eofT (bytesT [0])
|
||||
|
||||
, testCase "readTLVList: reads zero TLV entries" $ do
|
||||
let input = "readTLVList 0 [(1) (2) (3)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= okT (ofList []) (bytesT [1,2,3])
|
||||
|
||||
, testCase "readTLVList: reads one TLV entry and preserves rest" $ do
|
||||
-- tag=1, len=3, value="foo"
|
||||
let input = "readTLVList 1 [(0) (1) (0) (0) (0) (3) (102) (111) (111) (99) (111) (110) (116) (114) (101) (115) (116)]"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ rest)) -> do
|
||||
-- ok: value is list with one TLV, rest should be [(99)...]
|
||||
return ()
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 5: readManifest (full parser)
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "readManifest: parses a minimal manifest with no metadata" $ do
|
||||
let input = "readManifest " ++ bytesExpr fullMinimalManifestBytes
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ _)) -> return () -- ok result
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
, testCase "readManifest: preserves trailing extension bytes" $ do
|
||||
let input = "readManifest (append " ++ bytesExpr fullMinimalManifestBytes ++ " [(99) (111) (110) (116) (101) (110) (116) (101) (114)])"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork trueTag (Fork _ _)) | trueTag == trueT -> return ()
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 6: lookupMetadata
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "lookupMetadata: finds metadata by tag" $ do
|
||||
let tlv1 = makeTLVPair 1 "my-pkg"
|
||||
let tlv2 = makeTLVPair 2 "1.0"
|
||||
let input = "lookupMetadata (" ++ tlv1 ++ ") " ++ bytesExpr [(0), (1)]
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= justT (bytesT [109,121,45,112,107,103])
|
||||
|
||||
, testCase "lookupMetadata: returns nothing for unknown tag" $ do
|
||||
let tlv1 = makeTLVPair 1 "my-pkg"
|
||||
let input = "lookupMetadata " ++ tlv1 ++ " " ++ bytesExpr [(0), (2)]
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= nothingT
|
||||
|
||||
, testCase "lookupMetadata: returns nothing for empty list" $ do
|
||||
let input = "lookupMetadata [] " ++ bytesExpr [(0), (1)]
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
result env @?= nothingT
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 7: Export selection
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
-- Build export entry: (pair name (pair rootHash (pair kind abi)))
|
||||
-- Test: select export by explicit name ("main")
|
||||
, testCase "selectExport: finds export by explicit name" $ do
|
||||
let input = "selectExport " ++ multiExportExpr ++ " " ++ bytesExpr (map (fromIntegral . fromEnum) "main")
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ _)) -> return () -- ok result
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
-- Test: selectExport prefers "main" when no explicit name
|
||||
, testCase "selectExport: selects 'main' when no explicit name and 'main' exists" $ do
|
||||
let input = "selectExport " ++ multiExportExpr ++ " " ++ bytesExpr []
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ _)) -> return () -- ok result
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
-- Test: selectExport selects single export when only one exists
|
||||
, testCase "selectExport: auto-selects single export" $ do
|
||||
let input = "selectExport " ++ singleExportExpr ++ " " ++ bytesExpr []
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ _)) -> return () -- ok result
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
-- Test: getExportNames lists all export names
|
||||
, testCase "getExportNames: returns list of all export names" $ do
|
||||
let input = "getExportNames " ++ multiExportExpr
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
-- Should return a list of two byte strings
|
||||
case actualResult of
|
||||
(Fork (Fork _ _) (Fork (Fork _ _) _)) -> return () -- list with 2 items
|
||||
_ -> assertFailure $ "expected list of 2 items, got: " ++ show actualResult
|
||||
|
||||
-- Test: selectExport errors when multiple exports but no "main" and no explicit name
|
||||
, testCase "selectExport: errors with multiple exports but no 'main'" $ do
|
||||
let multiNoMain =
|
||||
"["
|
||||
++ exportEntryExpr "validate" (replicate 32 0) "term" "arboricx.abi.tree.v1"
|
||||
++ " "
|
||||
++ exportEntryExpr "test" (replicate 32 1) "term" "arboricx.abi.tree.v1"
|
||||
++ "]"
|
||||
let input = "selectExport " ++ multiNoMain ++ " " ++ bytesExpr []
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork falseT _) -> return () -- err result
|
||||
_ -> assertFailure $ "expected err result, got: " ++ show actualResult
|
||||
|
||||
-- Test: selectExportOpt works with Just bytes (explicit name given)
|
||||
, testCase "selectExportOpt: selects by explicit name when given" $ do
|
||||
let input = "selectExportOpt " ++ multiExportExpr ++ " " ++ bytesExpr (map (fromIntegral . fromEnum) "validate")
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ _)) -> return () -- ok result
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 8: validateManifestCore
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "validateManifestCore: passes on valid core" $ do
|
||||
let input = "matchResult (code rest : err code rest) (core rest : validateManifestCore core " ++ bytesExpr [(1), (2)] ++ ") (readManifestCore " ++ bytesExpr minimalManifestCoreBytes ++ ")"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork trueTag (Fork _ rest)) | trueTag == trueT -> rest @?= bytesT [1,2]
|
||||
_ -> assertFailure $ "expected ok result, got: " ++ show actualResult
|
||||
|
||||
, testCase "validateManifestCore: fails on wrong schema" $ do
|
||||
let badCoreBytes = take 16 minimalManifestCoreBytes ++ map (fromIntegral . fromEnum) "z" ++ drop 17 minimalManifestCoreBytes
|
||||
let input = "matchResult (code rest : err code rest) (core rest : validateManifestCore core " ++ bytesExpr [] ++ ") (readManifestCore " ++ bytesExpr badCoreBytes ++ ")"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork falseTag _) | falseTag == falseT -> return ()
|
||||
_ -> assertFailure $ "expected err result, got: " ++ show actualResult
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Step 9: readArboricxBundle (end-to-end with real fixture)
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "readArboricxBundle: parses id.arboricx fixture" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
case decodeBundle fixtureBytes of
|
||||
Left err -> assertFailure $ "decodeBundle failed: " ++ err
|
||||
Right bundle -> do
|
||||
let manifestBytes = bundleManifestBytes bundle
|
||||
-- The manifest section should be parseable
|
||||
let input = "readManifest " ++ bytesExpr (map toInteger (BS.unpack manifestBytes))
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork trueTag (Fork _ _)) | trueTag == trueT -> return ()
|
||||
_ -> assertFailure $ "readManifest failed on id.arboricx manifest: " ++ show actualResult
|
||||
|
||||
, testCase "readArboricxBundle: end-to-end bundle parse" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = "readArboricxBundle " ++ bytesExpr (map toInteger (BS.unpack fixtureBytes))
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork _ (Fork _ _)) -> return () -- ok: (pair validManifest afterManifest)
|
||||
_ -> assertFailure $ "readArboricxBundle failed: " ++ show actualResult
|
||||
|
||||
, testCase "readArboricxBundle: rejects bundle with wrong manifest core" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
-- Modify a byte in the manifest section to invalidate it
|
||||
-- The manifest starts at offset 152 in the bundle (from header dirOffset=32)
|
||||
-- Section directory: 2 entries * 60 = 120 bytes, starting at offset 32
|
||||
-- Manifest entry at directory offset 32: type(4) + version(2) + flags(2) + compression(2) + digestAlg(2) + offset(8) + length(8) + digest(32) = 60
|
||||
-- Manifest offset = 32 + 60 = 92
|
||||
-- The manifest itself starts at offset 152 (0x98)
|
||||
-- Change byte at position 152+8 = 160 from 'a' (97) to 'z' (122) to break the schema string
|
||||
let bs = map toInteger (BS.unpack fixtureBytes)
|
||||
let modifiedBs = take 160 bs ++ [122] ++ drop 161 bs
|
||||
let input = "readArboricxBundle " ++ bytesExpr modifiedBs
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let actualResult = result env
|
||||
case actualResult of
|
||||
(Fork falseT _) -> return () -- err result (validation failure)
|
||||
_ -> assertFailure $ "expected err result, got: " ++ show actualResult
|
||||
|
||||
-- ------------------------------------------------------------------------
|
||||
-- Comprehensive end-to-end: extract manifest fields and verify as strings
|
||||
-- ------------------------------------------------------------------------
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates manifest schema" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = extractManifestField fixtureBytes "manifestSchema"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let schemaT = result env
|
||||
toString schemaT @?= Right "arboricx.bundle.manifest.v1"
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates bundleType" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = extractManifestField fixtureBytes "manifestBundleType"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let bundleTypeT = result env
|
||||
toString bundleTypeT @?= Right "tree-calculus-executable-object"
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates runtime evaluation" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = extractManifestField fixtureBytes "manifestRuntimeEvaluation"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let evalT = result env
|
||||
toString evalT @?= Right "normal-order"
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates runtime ABI" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = extractManifestField fixtureBytes "manifestRuntimeAbi"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let abiT = result env
|
||||
toString abiT @?= Right "arboricx.abi.tree.v1"
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates root names" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = "matchResult "
|
||||
++ " (errCode rest : errCode) "
|
||||
++ " (bundleResult rest : "
|
||||
++ " matchPair "
|
||||
++ " (validCore metadataWithExtensions : "
|
||||
++ " matchList "
|
||||
++ " (err 99 t) " -- empty roots
|
||||
++ " (rootEntry rest : "
|
||||
++ " matchPair "
|
||||
++ " (_ roleField : roleField) "
|
||||
++ " rootEntry) "
|
||||
++ " (manifestRoots validCore)) "
|
||||
++ " bundleResult) "
|
||||
++ " (readArboricxBundle " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let rootRoleT = result env
|
||||
-- Should find at least one root with a role (either "default" or "root")
|
||||
case toString rootRoleT of
|
||||
Right role -> assertBool "root role should be 'default' or 'root'"
|
||||
(role == "default" || role == "root")
|
||||
Left err -> assertFailure $ "failed to extract root role: " ++ err
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates closure" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = "matchResult "
|
||||
++ " (errCode rest : errCode) "
|
||||
++ " (bundleResult rest : "
|
||||
++ " matchPair "
|
||||
++ " (validCore metadataWithExtensions : "
|
||||
++ " matchPair "
|
||||
++ " (closure _ : closure) "
|
||||
++ " (manifestClosureByte validCore)) "
|
||||
++ " bundleResult) "
|
||||
++ " (readArboricxBundle " ++ bytesExpr (map toInteger $ BS.unpack fixtureBytes) ++ ")"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let closureT = result env
|
||||
case toNumber closureT of
|
||||
Right 0 -> return ()
|
||||
Right n -> assertFailure $ "closure should be 0, got " ++ show n
|
||||
Left err -> assertFailure $ "failed to extract closure: " ++ err
|
||||
|
||||
, testCase "readArboricxBundle: extracts and validates hash algorithm" $ do
|
||||
fixtureBytes <- BS.readFile "test/fixtures/id.arboricx"
|
||||
let input = extractManifestField fixtureBytes "manifestTreeHashAlgorithm"
|
||||
library <- evaluateFile "./lib/arboricx.tri"
|
||||
let env = evalTricu library (parseTricu input)
|
||||
let algoT = result env
|
||||
toString algoT @?= Right "sha256"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user