@@ -375,14 +375,16 @@ def test_source_normalization_hash_and_positions_are_stable() -> None:
375375 assert compilation .frontend_contract == "openstatspec-spss-syntax-frontend-v0.2"
376376
377377
378- def test_schema_commands_use_the_v03_frontend_contract () -> None :
378+ def test_schema_commands_use_python_extension_contracts () -> None :
379379 compilation = compile_spss_syntax (
380380 "STRING note (A4)." ,
381381 _schema (VariableDefinition ("q1" , "numeric" )),
382382 )
383383
384- assert compilation .plan .contract == "openstatspec-transformation-plan-v0.3"
385- assert compilation .frontend_contract == "openstatspec-spss-syntax-frontend-v0.3"
384+ assert compilation .plan .contract == "openstatspec-python-schema-change-plan-v0.1"
385+ assert compilation .frontend_contract == "openstatspec-python-schema-change-spss-v0.1"
386+ assert transform_module .TRANSFORMATION_PLAN_SCHEMA_CHANGE_CONTRACT == compilation .plan .contract
387+ assert transform_module .SPSS_FRONTEND_SCHEMA_CHANGE_CONTRACT == compilation .frontend_contract
386388
387389def test_string_comparison_fails_closed_until_exact_collation_is_supported () -> None :
388390 error = _error (
@@ -584,26 +586,49 @@ def test_v02_plan_and_schema_reject_decimal_format_that_cannot_fit() -> None:
584586 )
585587
586588
587- def test_schema_operations_require_v03_contract () -> None :
588- operation = CreateVariableOperation ("note" , "string" , 8 )
589+ @pytest .mark .parametrize ("contract" , [
590+ "openstatspec-transformation-plan-v0.1" ,
591+ "openstatspec-transformation-plan-v0.2" ,
592+ ])
593+ @pytest .mark .parametrize ("operation" , [
594+ CreateVariableOperation ("note" , "string" , 8 ), DeleteVariableOperation ("note" ),
595+ ])
596+ def test_official_plan_contracts_reject_schema_operations (contract , operation ) -> None :
589597 with pytest .raises (TransformationFrontendError ) as caught :
590- TransformationPlan ((operation ,))
598+ transformation_plan_from_dict ({
599+ "contract" : contract , "input_alias" : "parent" ,
600+ "operations" : [operation .as_dict ()],
601+ })
591602 assert caught .value .code == "invalid_transformation_plan"
592603
593- plan = TransformationPlan (
594- (operation ,), contract = "openstatspec-transformation-plan-v0.3" ,
604+
605+ def test_legacy_schema_plan_roundtrip_preserves_exact_hash () -> None :
606+ serialized = (
607+ '{"contract":"openstatspec-transformation-plan-v0.3","input_alias":"parent",'
608+ '"operations":[{"declared_string_width":4,"op":"create_variable",'
609+ '"storage_kind":"string","variable":"note"}]}'
595610 )
596- assert plan .contract == "openstatspec-transformation-plan-v0.3"
611+ raw = json .loads (serialized )
612+ legacy = transformation_plan_from_dict (raw )
613+ assert canonical_plan_json (raw ) == legacy .canonical_json () == serialized
614+ assert canonical_plan_hash (raw ) == legacy .sha256 () == (
615+ "3c1933c6df1ea664d506d3b7879af5141f912181884129d2861bf19e44f4e131"
616+ )
617+ new = transformation_plan_from_dict ({
618+ ** raw , "contract" : "openstatspec-python-schema-change-plan-v0.1" ,
619+ })
620+ assert new .operations == legacy .operations
621+ assert new .sha256 () != legacy .sha256 ()
597622
598623
599- def test_spss_schema_commands_emit_v03_contract () -> None :
624+ def test_spss_schema_commands_emit_python_extension_contract () -> None :
600625 schema = _schema (VariableDefinition ("q1" , "numeric" ))
601626 assert bind_spss_syntax (
602627 parse_spss_syntax ("STRING note (A8)." ), schema ,
603- ).plan .contract == "openstatspec-transformation- plan-v0.3 "
628+ ).plan .contract == "openstatspec-python-schema-change- plan-v0.1 "
604629 assert bind_spss_syntax (
605630 parse_spss_syntax ("COMPUTE other = q1. DELETE VARIABLES q1." ), schema ,
606- ).plan .contract == "openstatspec-transformation- plan-v0.3 "
631+ ).plan .contract == "openstatspec-python-schema-change- plan-v0.1 "
607632
608633
609634def test_custom_nonempty_input_alias_is_canonical () -> None :
0 commit comments