test_domain.mojo (21865B)
1 from std.testing import TestSuite, assert_equal, assert_raises, assert_true 2 3 from hyf_core.domain.evidence import EvidenceRef 4 from hyf_core.domain.source import ( 5 actor_id, 6 farm_id, 7 lot_id, 8 need_id, 9 revision, 10 source_id, 11 trusted_source, 12 ) 13 14 15 def test_source_identity_accepts_valid_and_rejects_empty() raises: 16 assert_equal(source_id("s1").value, "s1") 17 assert_equal(actor_id("a1").value, "a1") 18 assert_equal(farm_id("f1").value, "f1") 19 assert_equal(need_id("n1").value, "n1") 20 assert_equal(lot_id("l1").value, "l1") 21 assert_equal(revision("r1").value, "r1") 22 with assert_raises(): 23 _ = source_id("") 24 with assert_raises(): 25 _ = revision(" r1") 26 with assert_raises(): 27 _ = actor_id("a1 ") 28 29 30 def test_trusted_source_preserves_revision() raises: 31 var source = trusted_source("s1", "r1", "a1", "f1") 32 assert_equal(source.source_id.value, "s1") 33 assert_equal(source.revision.value, "r1") 34 assert_equal(source.actor_id.value, "a1") 35 assert_equal(source.farm_id.value, "f1") 36 37 38 def main() raises: 39 TestSuite.discover_tests[__functions_in_module()]().run() 40 41 42 from hyf_core.domain.evidence import ( 43 record_field_evidence, 44 selected_text, 45 span_evidence, 46 span_is_valid, 47 validate_span_against_revision, 48 ) 49 50 51 def test_evidence_span_byte_offsets_and_revision() raises: 52 var text = "caf\u00e9: 80 lb" 53 var evidence = span_evidence("s1", "r1", 7, 12, "span") 54 assert_equal(evidence.kind, "span") 55 assert_equal(selected_text(text, evidence), "80 lb") 56 validate_span_against_revision(text, "r1", evidence) 57 assert_true(span_is_valid(text, evidence)) 58 59 with assert_raises(): 60 _ = span_evidence("s1", "r1", 5, 4, "span") 61 62 63 def test_evidence_rejects_out_of_range_and_revision_mismatch() raises: 64 var text = "short" 65 with assert_raises(): 66 _ = span_evidence("s1", "r1", -1, 2, "span") 67 var beyond = span_evidence("s1", "r1", 0, 99, "span") 68 with assert_raises(): 69 validate_span_against_revision(text, "r1", beyond) 70 var good = span_evidence("s1", "r1", 0, 3, "span") 71 with assert_raises(): 72 validate_span_against_revision(text, "r2", good) 73 var field = record_field_evidence( 74 "s1", "r1", "lot-1", "unreserved_quantity" 75 ) 76 assert_equal(field.kind, "record_field") 77 assert_equal(field.record_id.value(), "lot-1") 78 79 80 from hyf_core.domain.field import ( 81 DecodedField, 82 field_is_known, 83 field_known_or, 84 known_approximate_field, 85 known_field, 86 unresolved_field, 87 validate_field_consistency, 88 ) 89 90 91 def test_field_known_zero_distinct_from_unknown_and_false() raises: 92 var zero = known_field("0", "span") 93 assert_true(field_is_known(zero)) 94 assert_equal(field_known_or(zero, "?"), "0") 95 var false_value = known_field("false", "model") 96 assert_true(field_is_known(false_value)) 97 var unknown = unresolved_field("span") 98 assert_true(not field_is_known(unknown)) 99 assert_equal(field_known_or(unknown, "?"), "?") 100 validate_field_consistency(zero) 101 validate_field_consistency(unknown) 102 var approximate = known_approximate_field("80", "span") 103 assert_equal(approximate.qualifier, "approximate") 104 105 106 from hyf_core.domain.quantity import ( 107 new_quantity, 108 quantity_add, 109 quantity_compare, 110 quantity_is_negative, 111 quantity_rescale, 112 ) 113 114 115 def test_quantity_exact_compare_and_scale() raises: 116 var a = new_quantity(8000, 2, "kg", "mass", "exact") 117 var b = new_quantity(80, 0, "kg", "mass", "exact") 118 assert_equal(quantity_compare(a, b), 0) 119 var c = new_quantity(50, 0, "kg", "mass", "exact") 120 assert_equal(quantity_compare(c, b), -1) 121 assert_equal(quantity_rescale(b, 2).value, 8000) 122 123 124 def test_quantity_add_overflow_and_approximation() raises: 125 var approx = new_quantity(80, 0, "lb", "mass", "approximate") 126 var exact = new_quantity(5, 0, "lb", "mass", "exact") 127 var total = quantity_add(approx, exact) 128 assert_equal(total.value, 85) 129 assert_equal(total.qualifier, "approximate") 130 var huge = new_quantity(9223372036854775807, 0, "kg", "mass", "exact") 131 with assert_raises(): 132 _ = quantity_add(huge, exact) 133 var adjustment = new_quantity(-5, 0, "kg", "mass", "exact") 134 assert_true(quantity_is_negative(adjustment)) 135 136 137 from hyf_core.domain.units import ( 138 apply_conversion, 139 conversion_rule, 140 unit_dimension, 141 ) 142 143 144 def test_unit_dimensions_and_exact_mass_conversion() raises: 145 assert_equal(unit_dimension("lb"), "mass") 146 assert_equal(unit_dimension("ml"), "volume") 147 assert_equal(unit_dimension("widget"), "unknown") 148 149 var pounds = new_quantity(80, 0, "lb", "mass", "exact") 150 var rule = conversion_rule("lb", "kg", 45359237, 100000000, "test-1") 151 var kilograms = apply_conversion(pounds, rule) 152 assert_equal(kilograms.unit, "kg") 153 assert_equal(kilograms.dimension, "mass") 154 assert_equal(kilograms.value, 3628738960) 155 assert_equal(kilograms.scale, 8) 156 157 158 def test_unit_conversion_rejects_dimension_mismatch_and_unknown_denominator() raises: 159 with assert_raises(): 160 _ = conversion_rule("lb", "l", 1, 1, "v1") 161 with assert_raises(): 162 _ = conversion_rule("lb", "kg", 1, 3, "v1") 163 var pounds = new_quantity(1, 0, "lb", "mass", "exact") 164 var kg_rule = conversion_rule("lb", "kg", 45359237, 100000000, "test-1") 165 with assert_raises(): 166 _ = apply_conversion(new_quantity(1, 0, "kg", "mass", "exact"), kg_rule) 167 _ = pounds 168 169 170 from hyf_core.domain.pack import apply_pack_conversion, pack_rule 171 172 173 def test_pack_conversion_known_and_rejections() raises: 174 var boxes = new_quantity(20, 0, "box", "count", "exact") 175 var rule = pack_rule("tomato.roma", "box", "kg", 5, 0, "pack-1") 176 var mass = apply_pack_conversion("tomato.roma", boxes, rule) 177 assert_equal(mass.value, 100) 178 assert_equal(mass.unit, "kg") 179 assert_equal(mass.dimension, "mass") 180 181 with assert_raises(): 182 _ = apply_pack_conversion("carrot", boxes, rule) 183 with assert_raises(): 184 _ = apply_pack_conversion( 185 "tomato.roma", new_quantity(1, 0, "kg", "mass", "exact"), rule 186 ) 187 with assert_raises(): 188 _ = pack_rule("tomato.roma", "box", "mystery", 5, 0, "pack-1") 189 190 191 from hyf_core.domain.units import normalize_quantity_with_rule 192 193 194 def test_normalization_preserves_approximation() raises: 195 var approximate = new_quantity(80, 0, "lb", "mass", "approximate") 196 var rule = conversion_rule("lb", "kg", 45359237, 100000000, "test-1") 197 var normalized = normalize_quantity_with_rule(approximate, rule) 198 assert_equal(normalized.qualifier, "approximate") 199 assert_equal(normalized.unit, "kg") 200 var exact = new_quantity(80, 0, "lb", "mass", "exact") 201 assert_equal(normalize_quantity_with_rule(exact, rule).qualifier, "exact") 202 203 204 from hyf_core.domain.price import ( 205 known_price, 206 price_compare, 207 price_is_known, 208 unknown_price, 209 ) 210 211 212 def test_price_unknown_distinct_from_zero_and_no_fx() raises: 213 var unknown = unknown_price() 214 assert_true(not price_is_known(unknown)) 215 assert_equal(unknown.amount, 0) 216 var zero = known_price(0, 2, "CAD", "per_unit") 217 assert_true(price_is_known(zero)) 218 var five = known_price(500, 2, "CAD", "per_unit") 219 assert_equal(price_compare(five, zero), 1) 220 with assert_raises(): 221 _ = price_compare(five, unknown) 222 var usd = known_price(500, 2, "USD", "per_unit") 223 with assert_raises(): 224 _ = price_compare(five, usd) 225 with assert_raises(): 226 _ = known_price(1, 2, "", "per_unit") 227 with assert_raises(): 228 _ = known_price(1, 2, "CAD", "per_kg_approx") 229 230 231 from hyf_core.domain.time import ( 232 date_only, 233 time_context, 234 timestamp, 235 timestamp_has_zone, 236 zoneless_timestamp, 237 ) 238 239 240 def test_time_types_distinguish_source_and_evaluation() raises: 241 var source = timestamp(1789000000, "America/Vancouver") 242 var ingestion = timestamp(1789000100, "America/Vancouver") 243 var replay_later = timestamp(1789900000, "America/Vancouver") 244 var context = time_context(source, ingestion, replay_later) 245 assert_true(timestamp_has_zone(context.source_time)) 246 assert_true(timestamp_has_zone(context.evaluation_time)) 247 assert_true( 248 context.evaluation_time.epoch_seconds 249 > context.source_time.epoch_seconds 250 ) 251 var d = date_only(2026, 9, 25) 252 assert_equal(d.year, 2026) 253 assert_equal(d.month, 9) 254 assert_equal(d.day, 25) 255 var zoneless = zoneless_timestamp(1789000000) 256 assert_true(not timestamp_has_zone(zoneless)) 257 with assert_raises(): 258 _ = time_context(zoneless, ingestion, replay_later) 259 with assert_raises(): 260 _ = date_only(2026, 13, 1) 261 with assert_raises(): 262 _ = timestamp(1, "") 263 264 265 from hyf_core.normalization.dates import ( 266 resolve_relative_expression, 267 resolve_weekday, 268 weekday_index, 269 ) 270 271 272 def test_source_anchored_relative_date_replay() raises: 273 var monday = date_only(2026, 9, 21) 274 assert_equal(weekday_index(monday), 0) 275 var friday = resolve_relative_expression("Friday", monday) 276 assert_equal(friday.resolution, "resolved") 277 assert_equal(friday.date.year, 2026) 278 assert_equal(friday.date.month, 9) 279 assert_equal(friday.date.day, 25) 280 var same = resolve_weekday(monday, "Monday", "on_or_after") 281 assert_equal(same.date.day, 21) 282 var next = resolve_weekday(monday, "Monday", "next") 283 assert_equal(next.date.day, 28) 284 # Replay at a later evaluation time uses the same source reference. 285 assert_equal(resolve_relative_expression("Friday", monday).date.day, 25) 286 287 288 from hyf_core.normalization.dates import ( 289 LocalTimeResolution, 290 resolve_local_time, 291 window_contains, 292 ) 293 294 295 def test_local_time_ambiguity_and_window_boundaries() raises: 296 var offsets = List[Int]() 297 offsets.append(-420) 298 offsets.append(-480) 299 var ambiguous = resolve_local_time(offsets) 300 assert_equal(ambiguous.resolution, "unresolved") 301 assert_equal(ambiguous.ambiguity, "ambiguous_local") 302 303 var empty = List[Int]() 304 assert_equal(resolve_local_time(empty).ambiguity, "missing_zone") 305 306 var single = List[Int]() 307 single.append(-420) 308 assert_equal(resolve_local_time(single).resolution, "resolved") 309 310 assert_true(window_contains(20717, 20721, "exclusive", 20721) == False) 311 assert_true(window_contains(20717, 20721, "inclusive", 20721)) 312 assert_true(window_contains(20717, 20721, "exclusive", 20720)) 313 with assert_raises(): 314 _ = window_contains(1, 2, "half-open", 1) 315 316 317 from hyf_core.domain.freshness import ( 318 freshness, 319 record_recency_implies_harvest_age, 320 ) 321 322 323 def test_freshness_separates_record_harvest_and_verification() raises: 324 var value = freshness(5, 240, 60) 325 assert_equal(value.record_age_minutes, 5) 326 assert_equal(value.harvest_age_minutes, 240) 327 assert_equal(value.verification_age_minutes, 60) 328 assert_true(not record_recency_implies_harvest_age()) 329 with assert_raises(): 330 _ = freshness(-1, 0, 0) 331 332 333 from hyf_core.domain.product import ( 334 product_is_resolved, 335 product_matches, 336 resolved_product, 337 unresolved_product, 338 ) 339 340 341 def test_product_reference_resolution_and_unknown() raises: 342 var tomato = resolved_product("Roma tomatoes", "tomato.roma") 343 var other = resolved_product("Roma tomato", "tomato.roma") 344 assert_true(product_is_resolved(tomato)) 345 assert_true(product_matches(tomato, other)) 346 var unknown = unresolved_product("mystery greens") 347 assert_true(not product_is_resolved(unknown)) 348 assert_true(not product_matches(tomato, unknown)) 349 with assert_raises(): 350 _ = resolved_product("x", "") 351 352 353 from hyf_core.domain.supply_claim import ( 354 claim_confirms_inventory, 355 claim_is_forecast, 356 supply_claim, 357 ) 358 359 360 def test_supply_claims_are_per_product_and_not_inventory() raises: 361 var tomatoes = resolved_product("Roma tomatoes", "tomato.roma") 362 var basil = resolved_product("Basil", "basil") 363 var tomato_claim = supply_claim( 364 "c1", tomatoes, "offered", "known", 80, 0, "lb", "approximate" 365 ) 366 var basil_claim = supply_claim( 367 "c2", basil, "unavailable", "unknown", 0, 0, "", "unknown" 368 ) 369 assert_equal(tomato_claim.status, "offered") 370 assert_equal(basil_claim.status, "unavailable") 371 assert_true(not claim_confirms_inventory(tomato_claim)) 372 var forecast = supply_claim( 373 "c3", basil, "forecast", "unknown", 0, 0, "", "unknown" 374 ) 375 assert_true(claim_is_forecast(forecast)) 376 assert_true(not claim_is_forecast(tomato_claim)) 377 with assert_raises(): 378 _ = supply_claim( 379 "c4", tomatoes, "probably", "known", 1, 0, "kg", "exact" 380 ) 381 382 383 from hyf_core.domain.supply_change import ( 384 change_expands_to_farm, 385 proposed_change, 386 ) 387 388 389 def test_supply_change_operations_and_scoped_withdrawal() raises: 390 var addition = proposed_change( 391 "listing", Optional[String]("b1"), "addition", None 392 ) 393 assert_equal(addition.operation, "addition") 394 var remaining = proposed_change( 395 "listing", Optional[String]("b1"), "remaining", None 396 ) 397 assert_equal(remaining.operation, "remaining") 398 var replacement = proposed_change( 399 "listing", Optional[String]("b1"), "replacement", None 400 ) 401 assert_equal(replacement.operation, "replacement") 402 # Ambiguous withdrawal target must not expand to whole-farm stock. 403 var ambiguous = proposed_change("listing", None, "withdrawal", None) 404 assert_equal(ambiguous.operation, "unresolved") 405 assert_true(ambiguous.unresolved) 406 assert_true(not change_expands_to_farm(ambiguous)) 407 var correction = proposed_change("product", None, "correction", None) 408 assert_equal(correction.operation, "unresolved") 409 with assert_raises(): 410 _ = proposed_change("galaxy", Optional[String]("x"), "addition", None) 411 412 413 from hyf_core.domain.review import review_add, review_clear, review_required 414 415 416 def test_review_state_optional_price_does_not_block_withdrawal_does() raises: 417 var clear = review_clear() 418 assert_true(not clear.required) 419 var blocked = review_required("withdrawal.target", "ambiguous_target") 420 assert_true(blocked.required) 421 assert_equal(len(blocked.clarifications), 1) 422 var extended = review_add(blocked, "quantity.unreserved", "stock_unknown") 423 assert_equal(len(extended.clarifications), 2) 424 425 426 from hyf_core.domain.clarification import ( 427 apply_clarification, 428 clarification_evidence, 429 clarification_is_stale, 430 ) 431 432 433 def test_clarification_refines_without_overwriting_original() raises: 434 var original = known_field("80", "span") 435 var evidence = clarification_evidence( 436 "quantity.unreserved", "farm-source-1", "r2", "60 lb unreserved" 437 ) 438 var refined = apply_clarification(original, evidence, "60") 439 assert_equal(original.value.value(), "80") 440 assert_equal(refined.value.value(), "60") 441 assert_true(not clarification_is_stale(evidence, "r2")) 442 assert_true(clarification_is_stale(evidence, "r3")) 443 444 445 from hyf_core.domain.demand import ( 446 Condition, 447 DemandLine, 448 condition, 449 condition_is_exclusion, 450 demand_line, 451 missing_information_is_prohibition, 452 ) 453 454 455 def test_demand_lines_and_condition_strength() raises: 456 var tomatoes = resolved_product("tomatoes", "tomato") 457 var lines = List[DemandLine]() 458 var conditions_a = List[Condition]() 459 conditions_a.append( 460 condition("fulfillment", "mandatory", Optional[String]("delivery")) 461 ) 462 conditions_a.append( 463 condition( 464 "fulfillment_excluded", "excluded", Optional[String]("pickup") 465 ) 466 ) 467 lines.append(demand_line("l1", tomatoes, "known", 25, 0, conditions_a)) 468 var conditions_b = List[Condition]() 469 conditions_b.append( 470 condition("grade", "permitted", Optional[String]("seconds")) 471 ) 472 lines.append(demand_line("l2", tomatoes, "unknown", 0, 0, conditions_b)) 473 assert_equal(len(lines), 2) 474 assert_true(condition_is_exclusion(lines[0].conditions[1])) 475 assert_true(not condition_is_exclusion(lines[0].conditions[0])) 476 assert_true(not missing_information_is_prohibition()) 477 with assert_raises(): 478 _ = condition("grade", "maybe", Optional[String]("x")) 479 480 481 from hyf_core.domain.snapshot import ( 482 SupplySnapshot, 483 snapshot_identity, 484 snapshot_unreserved_known, 485 supply_snapshot, 486 ) 487 488 489 def test_supply_snapshot_revisions_and_unknown_unreserved() raises: 490 var tomatoes = resolved_product("Roma tomatoes", "tomato.roma") 491 var known = supply_snapshot( 492 "lot-1", "l1", "farm-1", tomatoes, "known", 50, 0, "kg" 493 ) 494 var other_revision = supply_snapshot( 495 "lot-1", "l2", "farm-1", tomatoes, "known", 30, 0, "kg" 496 ) 497 assert_true(snapshot_unreserved_known(known)) 498 assert_true(snapshot_identity(known) != snapshot_identity(other_revision)) 499 var unknown = supply_snapshot( 500 "lot-2", "l1", "farm-1", tomatoes, "unknown", 0, 0, "kg" 501 ) 502 assert_true(not snapshot_unreserved_known(unknown)) 503 504 505 from hyf_core.domain.eligibility import ( 506 ConstraintAssessment, 507 compose_eligibility, 508 constraint_assessment, 509 ) 510 511 512 def test_eligibility_precedence() raises: 513 var checks = List[ConstraintAssessment]() 514 checks.append( 515 constraint_assessment("product", "pass", True, "product_mismatch") 516 ) 517 checks.append( 518 constraint_assessment("quantity", "unknown", True, "stock_unknown") 519 ) 520 checks.append( 521 constraint_assessment( 522 "preference", "unknown", False, "evidence_insufficient" 523 ) 524 ) 525 assert_equal(compose_eligibility(checks), "conditional") 526 527 var failing = List[ConstraintAssessment]() 528 failing.append( 529 constraint_assessment("quantity", "fail", True, "quantity_insufficient") 530 ) 531 failing.append( 532 constraint_assessment("window", "unknown", True, "window_mismatch") 533 ) 534 assert_equal(compose_eligibility(failing), "ineligible") 535 536 var optional_only = List[ConstraintAssessment]() 537 optional_only.append( 538 constraint_assessment( 539 "preference", "unknown", False, "evidence_insufficient" 540 ) 541 ) 542 assert_equal(compose_eligibility(optional_only), "eligible") 543 with assert_raises(): 544 _ = constraint_assessment("product", "maybe", True, "x") 545 546 547 from hyf_core.domain.plan import ( 548 Allocation, 549 LotCapacity, 550 MatchPlan, 551 allocation, 552 match_plan, 553 plan_conservation_violations, 554 ) 555 556 557 def test_plan_conservation_across_lots() raises: 558 var lots = List[LotCapacity]() 559 lots.append(LotCapacity(lot_id="lot-1", revision="l1", value=30, scale=0)) 560 lots.append(LotCapacity(lot_id="lot-2", revision="l1", value=40, scale=0)) 561 var allocations = List[Allocation]() 562 allocations.append(allocation("lot-1", "l1", "line-1", 30, 0)) 563 allocations.append(allocation("lot-2", "l1", "line-1", 40, 0)) 564 var plan = match_plan("p1", "farm-1", allocations) 565 assert_equal(len(plan_conservation_violations(plan, lots)), 0) 566 567 var over = List[Allocation]() 568 over.append(allocation("lot-1", "l1", "line-1", 31, 0)) 569 var bad = match_plan("p2", "farm-1", over) 570 assert_true(len(plan_conservation_violations(bad, lots)) > 0) 571 572 var shared = List[Allocation]() 573 shared.append(allocation("lot-1", "l1", "line-1", 20, 0)) 574 shared.append(allocation("lot-1", "l1", "line-2", 20, 0)) 575 var shared_plan = match_plan("p3", "farm-1", shared) 576 assert_true(len(plan_conservation_violations(shared_plan, lots)) > 0) 577 578 579 from hyf_core.domain.coverage import ( 580 Coverage, 581 coverage, 582 no_match_is_global_absence, 583 unsupported_is_no_supply, 584 ) 585 586 587 def test_coverage_limitations_do_not_claim_global_absence() raises: 588 var unsupported = List[String]() 589 unsupported.append("multi_supplier") 590 var value = coverage( 591 True, 10, 2, "single_supplier_compatible_lots", unsupported 592 ) 593 assert_equal(value.scope, "supplied_only") 594 assert_true(value.truncated) 595 assert_equal(len(value.unsupported), 1) 596 assert_true(not no_match_is_global_absence()) 597 assert_true(not unsupported_is_no_supply()) 598 599 600 from hyf_core.domain.execution import ( 601 ExecutionMeta, 602 execution_is_business_outcome, 603 execution_meta, 604 ) 605 606 607 def test_execution_metadata_is_separate_from_business_outcome() raises: 608 var complete = execution_meta( 609 "complete", 0, None, Optional[String]("qb1"), None 610 ) 611 assert_equal(complete.status, "complete") 612 var degraded = execution_meta( 613 "degraded", 0, None, None, Optional[String]("provider_degraded") 614 ) 615 assert_equal(degraded.status, "degraded") 616 assert_true(not execution_is_business_outcome()) 617 with assert_raises(): 618 _ = execution_meta("degraded", 0, None, None, None) 619 with assert_raises(): 620 _ = execution_meta("weird", 0, None, None, None) 621 622 623 from hyf_core.normalization.candidates import ( 624 Candidate, 625 candidate_kinds, 626 discover_candidates, 627 ) 628 629 630 def test_candidate_discovery_spans_and_unknown_products() raises: 631 var products = List[String]() 632 products.append("roma tomatoes") 633 var units = List[String]() 634 units.append("lb") 635 var dates = List[String]() 636 dates.append("friday") 637 var candidates = discover_candidates( 638 "Got about 80 lb of Roma tomatoes. Can deliver Friday.", 639 products, 640 units, 641 dates, 642 ) 643 var kinds = candidate_kinds(candidates) 644 assert_true(len(kinds) == 3) 645 var saw_quantity = False 646 for candidate in candidates: 647 if candidate.kind == "quantity": 648 assert_equal(candidate.text, "80 lb") 649 saw_quantity = True 650 assert_true(saw_quantity) 651 652 var unknown_products = List[String]() 653 unknown_products.append("kohlrabi") 654 var empty_units = List[String]() 655 var empty_dates = List[String]() 656 assert_equal( 657 len( 658 discover_candidates( 659 "mystery greens", unknown_products, empty_units, empty_dates 660 ) 661 ), 662 0, 663 )