From 3d50b7c4013bcf6028fa49d3813e1478e7568a8a Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Mon, 9 Nov 2026 22:42:46 +0000 Subject: [PATCH 1/3] testdiff: leave repeated single values unnumbered --- .../volume_doesnot_exist/output.txt | 4 ++-- .../volume_not_deployed/output.txt | 4 ++-- .../deploy/readplan/whl-upload/output.txt | 8 +++---- .../output.txt | 4 ++-- .../out.state_after_migration.json | 4 ++-- .../default-python/out.state_original.json | 4 ++-- .../command/grants/out.original_state.json | 4 ++-- .../dashboards/detect-change/out.plan.json | 4 ++-- .../job_runs/retried_run_now/output.txt | 4 ++-- .../basic/out.second-plan.txt | 4 ++-- .../catalog-name/out.second-plan.json | 4 ++-- .../recreate/name-change/out.second-plan.json | 4 ++-- .../route-optimized/out.second-plan.json | 4 ++-- .../recreate/route-optimized/output.txt | 4 ++-- .../recreate/schema-name/out.second-plan.json | 4 ++-- .../table-prefix/out.second-plan.json | 4 ++-- .../update/ai-gateway/out.plan.json | 4 ++-- .../both_gateway_and_tags/out.plan.json | 4 ++-- .../update/config/out.plan.json | 4 ++-- .../update/email-notifications/out.plan.json | 4 ++-- .../update/tags/out.plan.json | 4 ++-- .../recreate/with_endpoint/output.txt | 4 ++-- .../volumes/change-comment/output.txt | 4 ++-- .../volumes/change-name/out.plan.json | 4 ++-- .../default-python/classic/out.plan_dev.json | 4 ++-- .../classic/out.requests.dev.txt | 4 ++-- acceptance/cmd/patchwhl/output.txt | 10 ++++---- .../RECORD | 10 ++++---- libs/testdiff/replacement.go | 24 +++++++++---------- libs/testdiff/replacement_test.go | 11 +++++++++ 30 files changed, 87 insertions(+), 76 deletions(-) diff --git a/acceptance/bundle/artifacts/artifact_path_with_volume/volume_doesnot_exist/output.txt b/acceptance/bundle/artifacts/artifact_path_with_volume/volume_doesnot_exist/output.txt index 369460360da..418af38d90c 100644 --- a/acceptance/bundle/artifacts/artifact_path_with_volume/volume_doesnot_exist/output.txt +++ b/acceptance/bundle/artifacts/artifact_path_with_volume/volume_doesnot_exist/output.txt @@ -6,7 +6,7 @@ "browse_only": false, "catalog_name": "main", "catalog_type": "MANAGED_CATALOG", - "created_at": [UNIX_TIME_MILLIS][0], + "created_at": [UNIX_TIME_MILLIS], "created_by": "[USERNAME]", "enable_predictive_optimization": "INHERIT", "full_name": "main.schema-[UNIQUE_NAME]", @@ -14,7 +14,7 @@ "name": "schema-[UNIQUE_NAME]", "owner": "[USERNAME]", "schema_id": "[UUID]", - "updated_at": [UNIX_TIME_MILLIS][0], + "updated_at": [UNIX_TIME_MILLIS], "updated_by": "[USERNAME]" } diff --git a/acceptance/bundle/artifacts/artifact_path_with_volume/volume_not_deployed/output.txt b/acceptance/bundle/artifacts/artifact_path_with_volume/volume_not_deployed/output.txt index d5788b0b3c5..3b4a0d17539 100644 --- a/acceptance/bundle/artifacts/artifact_path_with_volume/volume_not_deployed/output.txt +++ b/acceptance/bundle/artifacts/artifact_path_with_volume/volume_not_deployed/output.txt @@ -6,7 +6,7 @@ "browse_only": false, "catalog_name": "main", "catalog_type": "MANAGED_CATALOG", - "created_at": [UNIX_TIME_MILLIS][0], + "created_at": [UNIX_TIME_MILLIS], "created_by": "[USERNAME]", "enable_predictive_optimization": "INHERIT", "full_name": "main.schema-[UNIQUE_NAME]", @@ -14,7 +14,7 @@ "name": "schema-[UNIQUE_NAME]", "owner": "[USERNAME]", "schema_id": "[UUID]", - "updated_at": [UNIX_TIME_MILLIS][0], + "updated_at": [UNIX_TIME_MILLIS], "updated_by": "[USERNAME]" } diff --git a/acceptance/bundle/deploy/readplan/whl-upload/output.txt b/acceptance/bundle/deploy/readplan/whl-upload/output.txt index 48212c57e37..04d64fe158a 100644 --- a/acceptance/bundle/deploy/readplan/whl-upload/output.txt +++ b/acceptance/bundle/deploy/readplan/whl-upload/output.txt @@ -1,7 +1,7 @@ === bundle deploy uploads the wheel >>> [CLI] bundle deploy -Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl... +Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl... Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/whl-upload/default/files... Created jobs.my_job Files: 2 uploaded, 0 deleted @@ -10,7 +10,7 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged >>> print_requests.py //artifacts/.internal --del-field raw_body { "method": "POST", - "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl", + "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl", "q": { "overwrite": "true" } @@ -26,7 +26,7 @@ Destroy: 1 deleted === bundle deploy --plan also uploads the wheel >>> [CLI] bundle deploy --plan tmp.plan.json -Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl... +Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl... Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/whl-upload/default/files... Created jobs.my_job Files: 4 uploaded, 0 deleted @@ -35,7 +35,7 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged >>> print_requests.py //artifacts/.internal --del-field raw_body { "method": "POST", - "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl", + "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/whl-upload/default/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl", "q": { "overwrite": "true" } diff --git a/acceptance/bundle/integration_whl/serverless_extras_dynamic_version/output.txt b/acceptance/bundle/integration_whl/serverless_extras_dynamic_version/output.txt index ae76c6b53d9..61f8f061534 100644 --- a/acceptance/bundle/integration_whl/serverless_extras_dynamic_version/output.txt +++ b/acceptance/bundle/integration_whl/serverless_extras_dynamic_version/output.txt @@ -1,7 +1,7 @@ >>> [CLI] bundle deploy Building my_test_code... -Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl... +Uploading .databricks/bundle/default/patched_wheels/my_test_code_my_test_code/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl... Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... Created jobs.some_other_job Files: 13 uploaded, 0 deleted @@ -10,7 +10,7 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged === The environment dependency keeps its [train] extras suffix >>> [CLI] jobs get [SOME_OTHER_JOB_ID] [ - "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl[train]" + "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/artifacts/.internal/my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl[train]" ] >>> [CLI] bundle run some_other_job diff --git a/acceptance/bundle/migrate/command/default-python/out.state_after_migration.json b/acceptance/bundle/migrate/command/default-python/out.state_after_migration.json index 007548bcee5..1ab1c3eb693 100644 --- a/acceptance/bundle/migrate/command/default-python/out.state_after_migration.json +++ b/acceptance/bundle/migrate/command/default-python/out.state_after_migration.json @@ -50,7 +50,7 @@ "job_cluster_key": "job_cluster", "libraries": [ { - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "notebook_task": { @@ -67,7 +67,7 @@ "job_cluster_key": "job_cluster", "libraries": [ { - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "python_wheel_task": { diff --git a/acceptance/bundle/migrate/command/default-python/out.state_original.json b/acceptance/bundle/migrate/command/default-python/out.state_original.json index 049572d0136..fc567072200 100644 --- a/acceptance/bundle/migrate/command/default-python/out.state_original.json +++ b/acceptance/bundle/migrate/command/default-python/out.state_original.json @@ -188,7 +188,7 @@ "provider_config": [], "pypi": [], "requirements": "", - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "max_retries": 0, @@ -262,7 +262,7 @@ "provider_config": [], "pypi": [], "requirements": "", - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "max_retries": 0, diff --git a/acceptance/bundle/migrate/command/grants/out.original_state.json b/acceptance/bundle/migrate/command/grants/out.original_state.json index b83be57aff1..be1ff2b549a 100644 --- a/acceptance/bundle/migrate/command/grants/out.original_state.json +++ b/acceptance/bundle/migrate/command/grants/out.original_state.json @@ -172,7 +172,7 @@ "browse_only": false, "catalog_name": "main", "comment": "mycomment", - "created_at": [UNIX_TIME_MILLIS][0], + "created_at": [UNIX_TIME_MILLIS], "created_by": "[USERNAME]", "full_name": "main.schema_grants.mymodel", "id": "main.schema_grants.mymodel", @@ -186,7 +186,7 @@ ], "schema_name": "schema_grants", "storage_location": null, - "updated_at": [UNIX_TIME_MILLIS][0], + "updated_at": [UNIX_TIME_MILLIS], "updated_by": "[USERNAME]" }, "sensitive_attributes": [], diff --git a/acceptance/bundle/resources/dashboards/detect-change/out.plan.json b/acceptance/bundle/resources/dashboards/detect-change/out.plan.json index 74462c9c567..243ab19e66d 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/out.plan.json +++ b/acceptance/bundle/resources/dashboards/detect-change/out.plan.json @@ -65,8 +65,8 @@ "serialized_dashboard": { "action": "skip", "reason": "etag_based", - "old": "sha256:[HASH][0]", - "new": "sha256:[HASH][0]", + "old": "sha256:[HASH]", + "new": "sha256:[HASH]", "remote": "{}\n" }, "update_time": { diff --git a/acceptance/bundle/resources/job_runs/retried_run_now/output.txt b/acceptance/bundle/resources/job_runs/retried_run_now/output.txt index bf30c0cb2c0..a856ca44fc9 100644 --- a/acceptance/bundle/resources/job_runs/retried_run_now/output.txt +++ b/acceptance/bundle/resources/job_runs/retried_run_now/output.txt @@ -18,7 +18,7 @@ Resources: 2 created, 0 changed, 0 deleted, 0 unchanged "method": "POST", "path": "/api/2.2/jobs/run-now", "body": { - "idempotency_token": "[IDEMPOTENCY_TOKEN][0]", + "idempotency_token": "[IDEMPOTENCY_TOKEN]", "job_id": [MY_JOB_ID] } } @@ -26,7 +26,7 @@ Resources: 2 created, 0 changed, 0 deleted, 0 unchanged "method": "POST", "path": "/api/2.2/jobs/run-now", "body": { - "idempotency_token": "[IDEMPOTENCY_TOKEN][0]", + "idempotency_token": "[IDEMPOTENCY_TOKEN]", "job_id": [MY_JOB_ID] } } diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.txt index f8508239cfc..2e41a7138ee 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.txt @@ -5,11 +5,11 @@ json.serial = 1; json.plan.resources.model_serving_endpoints.my_endpoint.action = "recreate"; json.plan.resources.model_serving_endpoints.my_endpoint.new_state.value.name = "[ENDPOINT_NAME_2]"; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.description = ""; -json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.creation_timestamp = [UNIX_TIME_MILLIS][0]; +json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.creation_timestamp = [UNIX_TIME_MILLIS]; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.creator = "[USERNAME]"; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.description = ""; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.id = "[ENDPOINT_ID_1]"; -json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.last_updated_timestamp = [UNIX_TIME_MILLIS][0]; +json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.last_updated_timestamp = [UNIX_TIME_MILLIS]; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.name = "[ENDPOINT_NAME_1]"; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.permission_level = "CAN_MANAGE"; json.plan.resources.model_serving_endpoints.my_endpoint.remote_state.endpoint_details.route_optimized = false; diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.json index 89686b87922..b799c7c0858 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.json @@ -92,11 +92,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ORIGINAL_ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.json index 2a50d836ce7..f653ffd8fe7 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.json @@ -77,11 +77,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ORIGINAL_ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.json index b5555453219..5278563ba8a 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.json @@ -66,11 +66,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ORIGINAL_ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt index c0bc3f5ea66..2aa97e5cf60 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt @@ -66,11 +66,11 @@ Resources: 1 created, 0 changed, 1 deleted, 0 unchanged ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ORIGINAL_ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": true, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.json index 0a2bc7bee2d..46ac4881da9 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.json @@ -92,11 +92,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ORIGINAL_ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.json index 6d2c9deccb6..b96358e39ac 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.json @@ -92,11 +92,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ORIGINAL_ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.json b/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.json index afe9fa25886..9c9742032d5 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.json @@ -92,11 +92,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.json b/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.json index 32c30ee2612..3f81ebd49eb 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.json @@ -98,11 +98,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.json b/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.json index 5b90f2088d6..c63f48b6eaa 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.json @@ -83,11 +83,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.json b/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.json index 0f860ce7ac3..ee9b6cf4ff9 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.json @@ -87,7 +87,7 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "email_notifications": { @@ -96,7 +96,7 @@ ] }, "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.json index f8850241ddf..431bfea1267 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.json @@ -83,11 +83,11 @@ ] } }, - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "description": "", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "name": "[ENDPOINT_ID]", "permission_level": "CAN_MANAGE", "route_optimized": false, diff --git a/acceptance/bundle/resources/vector_search_indexes/recreate/with_endpoint/output.txt b/acceptance/bundle/resources/vector_search_indexes/recreate/with_endpoint/output.txt index 777eace89a2..a45a77cee95 100644 --- a/acceptance/bundle/resources/vector_search_indexes/recreate/with_endpoint/output.txt +++ b/acceptance/bundle/resources/vector_search_indexes/recreate/with_endpoint/output.txt @@ -31,7 +31,7 @@ Plan: 1 to add, 0 to change, 1 to delete, 1 unchanged } }, "remote_state": { - "creation_timestamp": [UNIX_TIME_MILLIS][0], + "creation_timestamp": [UNIX_TIME_MILLIS], "creator": "[USERNAME]", "endpoint_status": { "state": "ONLINE" @@ -39,7 +39,7 @@ Plan: 1 to add, 0 to change, 1 to delete, 1 unchanged "endpoint_type": "STANDARD", "endpoint_uuid": "[UUID]", "id": "[UUID]", - "last_updated_timestamp": [UNIX_TIME_MILLIS][0], + "last_updated_timestamp": [UNIX_TIME_MILLIS], "last_updated_user": "[USERNAME]", "name": "vs-endpoint-[UNIQUE_NAME]", "scaling_info": {} diff --git a/acceptance/bundle/resources/volumes/change-comment/output.txt b/acceptance/bundle/resources/volumes/change-comment/output.txt index 275ccb5f70d..65f4afdeded 100644 --- a/acceptance/bundle/resources/volumes/change-comment/output.txt +++ b/acceptance/bundle/resources/volumes/change-comment/output.txt @@ -43,14 +43,14 @@ Resources: 1 created, 0 changed, 0 deleted, 0 unchanged { "catalog_name": "main", "comment": "COMMENT1", - "created_at": [UNIX_TIME_MILLIS][0], + "created_at": [UNIX_TIME_MILLIS], "created_by": "[USERNAME]", "full_name": "main.myschema.myvolume", "name": "myvolume", "owner": "[USERNAME]", "schema_name": "myschema", "storage_location": "s3://[METASTORE_NAME]/metastore/[UUID]/volumes/[UUID]", - "updated_at": [UNIX_TIME_MILLIS][0], + "updated_at": [UNIX_TIME_MILLIS], "volume_id": "[UUID]", "volume_type": "MANAGED" } diff --git a/acceptance/bundle/resources/volumes/change-name/out.plan.json b/acceptance/bundle/resources/volumes/change-name/out.plan.json index eb9308f572b..118c5f66104 100644 --- a/acceptance/bundle/resources/volumes/change-name/out.plan.json +++ b/acceptance/bundle/resources/volumes/change-name/out.plan.json @@ -18,14 +18,14 @@ "remote_state": { "catalog_name": "main", "comment": "COMMENT1", - "created_at": [UNIX_TIME_MILLIS][0], + "created_at": [UNIX_TIME_MILLIS], "created_by": "[USERNAME]", "full_name": "main.myschema.myvolume", "name": "myvolume", "owner": "[USERNAME]", "schema_name": "myschema", "storage_location": "s3://[METASTORE_NAME]/metastore/[UUID]/volumes/[UUID]", - "updated_at": [UNIX_TIME_MILLIS][0], + "updated_at": [UNIX_TIME_MILLIS], "volume_id": "[UUID]", "volume_type": "MANAGED" }, diff --git a/acceptance/bundle/templates/default-python/classic/out.plan_dev.json b/acceptance/bundle/templates/default-python/classic/out.plan_dev.json index 12ce3f987f5..e68ec963a2f 100644 --- a/acceptance/bundle/templates/default-python/classic/out.plan_dev.json +++ b/acceptance/bundle/templates/default-python/classic/out.plan_dev.json @@ -55,7 +55,7 @@ "job_cluster_key": "job_cluster", "libraries": [ { - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "notebook_task": { @@ -72,7 +72,7 @@ "job_cluster_key": "job_cluster", "libraries": [ { - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "python_wheel_task": { diff --git a/acceptance/bundle/templates/default-python/classic/out.requests.dev.txt b/acceptance/bundle/templates/default-python/classic/out.requests.dev.txt index 43ac10e32b4..f6236811f38 100644 --- a/acceptance/bundle/templates/default-python/classic/out.requests.dev.txt +++ b/acceptance/bundle/templates/default-python/classic/out.requests.dev.txt @@ -140,7 +140,7 @@ "job_cluster_key": "job_cluster", "libraries": [ { - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "notebook_task": { @@ -157,7 +157,7 @@ "job_cluster_key": "job_cluster", "libraries": [ { - "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl" + "whl": "/Workspace/Users/[USERNAME]/.bundle/my_default_python/dev/artifacts/.internal/my_default_python-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl" } ], "python_wheel_task": { diff --git a/acceptance/cmd/patchwhl/output.txt b/acceptance/cmd/patchwhl/output.txt index ed36b234141..d6d9972aa2b 100644 --- a/acceptance/cmd/patchwhl/output.txt +++ b/acceptance/cmd/patchwhl/output.txt @@ -3,11 +3,11 @@ >>> setmtime.py [TIMESTAMP] my_test_code-0.0.1-py3-none-any.whl >>> [CLI] selftest patchwhl my_test_code-0.0.1-py3-none-any.whl -Warn: Patched whl: my_test_code-0.0.1-py3-none-any.whl -> my_test_code-0.0.1+[UNIX_TIME_NANOS][0]-py3-none-any.whl +Warn: Patched whl: my_test_code-0.0.1-py3-none-any.whl -> my_test_code-0.0.1+[UNIX_TIME_NANOS]-py3-none-any.whl >>> diff.py original output ---- original/my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/METADATA -+++ output/my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/METADATA +--- original/my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/METADATA ++++ output/my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/METADATA @@ -1,5 +1,5 @@ Metadata-Version: 2.1 Name: my-test-code @@ -15,8 +15,8 @@ Warn: Patched whl: my_test_code-0.0.1-py3-none-any.whl -> my_test_code-0.0.1+[UN +Version: 0.0.1+[UNIX_TIME_NANOS] Summary: my test wheel Home-page: https://databricks.com ---- original/my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/RECORD -+++ output/my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/RECORD +--- original/my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/RECORD ++++ output/my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/RECORD @@ -1,7 +1,7 @@ src/__init__.py,sha256=BRmKeYehopKv4NG_SFa7t6wn248RrPHJivu7DM1R-Rw,48 src/__main__.py,sha256=8TtsnLsaJEM35Y4L8ocrv-qfxusgYpRL2HPyYiabHng,242 diff --git a/acceptance/cmd/patchwhl/output/my_test_code-0.0.1+1741187253123456700.dist-info/RECORD b/acceptance/cmd/patchwhl/output/my_test_code-0.0.1+1741187253123456700.dist-info/RECORD index 010da2844b7..56678166ab0 100644 --- a/acceptance/cmd/patchwhl/output/my_test_code-0.0.1+1741187253123456700.dist-info/RECORD +++ b/acceptance/cmd/patchwhl/output/my_test_code-0.0.1+1741187253123456700.dist-info/RECORD @@ -1,7 +1,7 @@ src/__init__.py,sha256=BRmKeYehopKv4NG_SFa7t6wn248RrPHJivu7DM1R-Rw,48 src/__main__.py,sha256=8TtsnLsaJEM35Y4L8ocrv-qfxusgYpRL2HPyYiabHng,242 -my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/METADATA,sha256=H99P2vEwB_hBVPNtPwsXZotaDQzmWEGeSlOtMzWe62U,217 -my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 -my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/entry_points.txt,sha256=oDWOW9SsBlk4Uejj1ftYPBxfhJ5ZJctb4JOUIG1rc-4,34 -my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4 -my_test_code-0.0.1+[UNIX_TIME_NANOS][0].dist-info/RECORD,, +my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/METADATA,sha256=H99P2vEwB_hBVPNtPwsXZotaDQzmWEGeSlOtMzWe62U,217 +my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/entry_points.txt,sha256=oDWOW9SsBlk4Uejj1ftYPBxfhJ5ZJctb4JOUIG1rc-4,34 +my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4 +my_test_code-0.0.1+[UNIX_TIME_NANOS].dist-info/RECORD,, diff --git a/libs/testdiff/replacement.go b/libs/testdiff/replacement.go index a8cc18b2fb4..08a6c02e90e 100644 --- a/libs/testdiff/replacement.go +++ b/libs/testdiff/replacement.go @@ -58,25 +58,25 @@ func (r *ReplacementsContext) Replace(s string) string { continue } - matches := repl.Old.FindAllString(s, -1) - if len(matches) <= 1 { + // Assign a stable [N] suffix per distinct matched value, in order of first + // appearance. Number only when there is more than one distinct value: a lone + // value (even if repeated) stays unsuffixed, so the common single-match output + // is identical to the non-distinct case. + seen := make(map[string]int) + for _, match := range repl.Old.FindAllString(s, -1) { + if _, ok := seen[match]; !ok { + seen[match] = len(seen) + } + } + if len(seen) <= 1 { s = repl.Old.ReplaceAllString(s, repl.New) continue } - nextIndex := 0 - seen := make(map[string]int) s = repl.Old.ReplaceAllStringFunc(s, func(match string) string { - idx, ok := seen[match] - if !ok { - idx = nextIndex - seen[match] = idx - nextIndex++ - } - submatches := repl.Old.FindStringSubmatchIndex(match) replaced := repl.Old.ExpandString(nil, repl.New, match, submatches) - return string(replaced) + "[" + strconv.Itoa(idx) + "]" + return string(replaced) + "[" + strconv.Itoa(seen[match]) + "]" }) } return s diff --git a/libs/testdiff/replacement_test.go b/libs/testdiff/replacement_test.go index 507774c8d40..4aa8804e497 100644 --- a/libs/testdiff/replacement_test.go +++ b/libs/testdiff/replacement_test.go @@ -47,6 +47,17 @@ func TestReplacement_DistinctSingleMatch(t *testing.T) { assert.Equal(t, "[NUMBER]", got) } +func TestReplacement_DistinctSingleValueRepeated(t *testing.T) { + rc := ReplacementsContext{Repls: []Replacement{ + {Old: regexp.MustCompile(`\d+`), New: "[NUMBER]", Distinct: true}, + }} + + // A single distinct value, even repeated, stays unsuffixed: numbering only + // adds signal when there is more than one distinct value to tell apart. + got := rc.Replace("25\n25") + assert.Equal(t, "[NUMBER]\n[NUMBER]", got) +} + func TestReplaceAppliesInOrder(t *testing.T) { input := "A" From 5c0a683092d07caea8231f6fd11c4ac1239e1519 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Mon, 12 Oct 2026 11:47:46 +0000 Subject: [PATCH 2/3] acceptance: add UniqueUUID test.toml knob for distinct UUID numbering The built-in [UUID] replacement collapses every UUID to one token, so a golden cannot tell two distinct UUIDs apart (raised in review of #6995). Add a UniqueUUID test.toml knob (default false) that, when set, makes the built-in UUID replacement honor the existing Distinct numbering: distinct UUIDs in an output become [UUID][0], [UUID][1], ... in order of first appearance. Existing tests are unaffected (default off); a test opts in with UniqueUUID = true. Co-authored-by: Isaac --- acceptance/acceptance_test.go | 3 +- acceptance/internal/config.go | 7 +++++ acceptance/selftest/uuid/out.test.toml | 1 + acceptance/selftest/uuid/output.txt | 5 ++++ acceptance/selftest/uuid/script | 6 ++++ acceptance/selftest/uuid/test.toml | 2 ++ .../selftest/uuid_not_unique/out.test.toml | 1 + .../selftest/uuid_not_unique/output.txt | 4 +++ acceptance/selftest/uuid_not_unique/script | 3 ++ acceptance/selftest/uuid_not_unique/test.toml | 5 ++++ libs/testdiff/replacement.go | 8 +++-- libs/testdiff/replacement_test.go | 29 ++++++++++++++++++- 12 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 acceptance/selftest/uuid/out.test.toml create mode 100644 acceptance/selftest/uuid/output.txt create mode 100644 acceptance/selftest/uuid/script create mode 100644 acceptance/selftest/uuid/test.toml create mode 100644 acceptance/selftest/uuid_not_unique/out.test.toml create mode 100644 acceptance/selftest/uuid_not_unique/output.txt create mode 100644 acceptance/selftest/uuid_not_unique/script create mode 100644 acceptance/selftest/uuid_not_unique/test.toml diff --git a/acceptance/acceptance_test.go b/acceptance/acceptance_test.go index 3805300a8e7..8483c1229a0 100644 --- a/acceptance/acceptance_test.go +++ b/acceptance/acceptance_test.go @@ -953,7 +953,8 @@ func runTest(t *testing.T, cmd.Env = append(cmd.Env, "CLOUD_ENV_BASE="+envBase) // Must be added PrepareReplacementsUser, otherwise conflicts with [USERNAME] - testdiff.PrepareReplacementsUUID(t, &repls) + // UniqueUUID defaults to false: number distinct UUIDs only when a test opts in. + testdiff.PrepareReplacementsUUID(t, &repls, isTruePtr(config.UniqueUUID)) // User replacements: repls.Repls = append(repls.Repls, config.Repls...) diff --git a/acceptance/internal/config.go b/acceptance/internal/config.go index 6ea79a9578a..48f5995ee08 100644 --- a/acceptance/internal/config.go +++ b/acceptance/internal/config.go @@ -63,6 +63,13 @@ type TestConfig struct { // Old is a regexp, New is a replacement expression. Repls []testdiff.Replacement + // If true, distinct UUIDs in an output are numbered ([UUID][0], [UUID][1], ...) so + // the golden pins which value appears where. If absent, defaults to false and every + // UUID collapses to [UUID]. Enable it for tests that need to tell distinct UUIDs + // apart; leave it off when they appear in a non-deterministic order (the index + // assignment would be unstable). + UniqueUUID *bool + // List of server stubs to load. Example configuration: // // [[Server]] diff --git a/acceptance/selftest/uuid/out.test.toml b/acceptance/selftest/uuid/out.test.toml new file mode 100644 index 00000000000..18b1a88417e --- /dev/null +++ b/acceptance/selftest/uuid/out.test.toml @@ -0,0 +1 @@ +Cloud = false diff --git a/acceptance/selftest/uuid/output.txt b/acceptance/selftest/uuid/output.txt new file mode 100644 index 00000000000..4b7d487d39f --- /dev/null +++ b/acceptance/selftest/uuid/output.txt @@ -0,0 +1,5 @@ + +=== Distinct UUIDs are numbered +first: [UUID][0] +second: [UUID][1] +first again: [UUID][0] diff --git a/acceptance/selftest/uuid/script b/acceptance/selftest/uuid/script new file mode 100644 index 00000000000..4a123e5f405 --- /dev/null +++ b/acceptance/selftest/uuid/script @@ -0,0 +1,6 @@ +title "Distinct UUIDs are numbered\n" +# Distinct UUIDs get a stable [N] suffix in order of first appearance, so a golden +# pins which value appears where; a repeated UUID keeps its number. +echo "first: 11111111-1111-1111-1111-111111111111" +echo "second: 22222222-2222-2222-2222-222222222222" +echo "first again: 11111111-1111-1111-1111-111111111111" diff --git a/acceptance/selftest/uuid/test.toml b/acceptance/selftest/uuid/test.toml new file mode 100644 index 00000000000..23a178a1b99 --- /dev/null +++ b/acceptance/selftest/uuid/test.toml @@ -0,0 +1,2 @@ +# Opt in to UUID numbering so distinct UUIDs in the output can be told apart. +UniqueUUID = true diff --git a/acceptance/selftest/uuid_not_unique/out.test.toml b/acceptance/selftest/uuid_not_unique/out.test.toml new file mode 100644 index 00000000000..18b1a88417e --- /dev/null +++ b/acceptance/selftest/uuid_not_unique/out.test.toml @@ -0,0 +1 @@ +Cloud = false diff --git a/acceptance/selftest/uuid_not_unique/output.txt b/acceptance/selftest/uuid_not_unique/output.txt new file mode 100644 index 00000000000..864022e8059 --- /dev/null +++ b/acceptance/selftest/uuid_not_unique/output.txt @@ -0,0 +1,4 @@ + +=== UniqueUUID = false collapses every UUID to [UUID] +first: [UUID] +second: [UUID] diff --git a/acceptance/selftest/uuid_not_unique/script b/acceptance/selftest/uuid_not_unique/script new file mode 100644 index 00000000000..4778cf7c88a --- /dev/null +++ b/acceptance/selftest/uuid_not_unique/script @@ -0,0 +1,3 @@ +title "UniqueUUID = false collapses every UUID to [UUID]\n" +echo "first: 11111111-1111-1111-1111-111111111111" +echo "second: 22222222-2222-2222-2222-222222222222" diff --git a/acceptance/selftest/uuid_not_unique/test.toml b/acceptance/selftest/uuid_not_unique/test.toml new file mode 100644 index 00000000000..6f0c2d4c2ec --- /dev/null +++ b/acceptance/selftest/uuid_not_unique/test.toml @@ -0,0 +1,5 @@ +# Opt out of UUID numbering: every UUID collapses to [UUID], regardless of value. +# Use this for tests that emit multiple distinct UUIDs in a non-deterministic order +# (the [N] assignment would be unstable), or where the distinction is not worth the +# extra tokens. +UniqueUUID = false diff --git a/libs/testdiff/replacement.go b/libs/testdiff/replacement.go index 08a6c02e90e..26ea0669862 100644 --- a/libs/testdiff/replacement.go +++ b/libs/testdiff/replacement.go @@ -227,9 +227,13 @@ func PrepareReplacementsUser(t testutil.TestingT, r *ReplacementsContext, u iam. } } -func PrepareReplacementsUUID(t testutil.TestingT, r *ReplacementsContext) { +// PrepareReplacementsUUID registers the UUID replacement. When unique is true, +// distinct UUIDs in a single output are numbered ([UUID][0], [UUID][1], ...) so the +// golden pins which value appears where; a single distinct UUID stays [UUID], even +// if repeated. When false, every UUID collapses to [UUID] regardless of value. +func PrepareReplacementsUUID(t testutil.TestingT, r *ReplacementsContext, unique bool) { t.Helper() - r.append(uuidRegex, "[UUID]", 0) + r.Repls = append(r.Repls, Replacement{Old: uuidRegex, New: "[UUID]", Distinct: unique}) } func PrepareReplacementSdkVersion(t testutil.TestingT, r *ReplacementsContext) { diff --git a/libs/testdiff/replacement_test.go b/libs/testdiff/replacement_test.go index 4aa8804e497..910d004c871 100644 --- a/libs/testdiff/replacement_test.go +++ b/libs/testdiff/replacement_test.go @@ -24,11 +24,38 @@ func TestReplacement_Encoded(t *testing.T) { func TestReplacement_UUID(t *testing.T) { var repls ReplacementsContext - PrepareReplacementsUUID(t, &repls) + PrepareReplacementsUUID(t, &repls, true) + // A lone UUID stays unsuffixed. assert.Equal(t, "[UUID]", repls.Replace("123e4567-e89b-12d3-a456-426614174000")) } +func TestReplacement_UUIDUnique(t *testing.T) { + var repls ReplacementsContext + + PrepareReplacementsUUID(t, &repls, true) + + input := "a: 123e4567-e89b-12d3-a456-426614174000\n" + + "b: 00000000-0000-0000-0000-000000000001\n" + + "a again: 123e4567-e89b-12d3-a456-426614174000" + expected := "a: [UUID][0]\n" + + "b: [UUID][1]\n" + + "a again: [UUID][0]" + assert.Equal(t, expected, repls.Replace(input)) +} + +func TestReplacement_UUIDNotUnique(t *testing.T) { + var repls ReplacementsContext + + PrepareReplacementsUUID(t, &repls, false) + + input := "a: 123e4567-e89b-12d3-a456-426614174000\n" + + "b: 00000000-0000-0000-0000-000000000001" + expected := "a: [UUID]\n" + + "b: [UUID]" + assert.Equal(t, expected, repls.Replace(input)) +} + func TestReplacement_Distinct(t *testing.T) { rc := ReplacementsContext{Repls: []Replacement{ {Old: regexp.MustCompile(`\d+`), New: "[NUMBER]", Distinct: true}, From d9d1f41e8438094bf444c02cab3781517e2cb094 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Mon, 9 Nov 2026 23:21:34 +0000 Subject: [PATCH 3/3] acceptance: support distinct UUIDs in plan and template comparisons --- acceptance/bin/repls.py | 40 ++++++++++++---- .../immutable/no-artifacts/out.plan.txt | 46 +++++++++++++++++++ .../deploy/immutable/no-artifacts/output.txt | 46 ------------------- .../deploy/immutable/no-artifacts/script | 10 ++-- 4 files changed, 84 insertions(+), 58 deletions(-) create mode 100644 acceptance/bundle/deploy/immutable/no-artifacts/out.plan.txt diff --git a/acceptance/bin/repls.py b/acceptance/bin/repls.py index 338127cf949..f24da6ce638 100644 --- a/acceptance/bin/repls.py +++ b/acceptance/bin/repls.py @@ -29,7 +29,7 @@ def read_entries(): def read_repls(): - """Return (pattern, replacement) pairs in the order they must be applied.""" + """Return (pattern, replacement, distinct) tuples in application order.""" entries = [] for item in read_entries(): @@ -38,35 +38,57 @@ def read_repls(): literal = item.get("Literal") if literal is None: - # "Distinct" is not honoured here; unlike the harness, we do not number the matches. - entries.append((order, item["Old"], new)) + entries.append((order, item["Old"], new, item["Distinct"])) continue # Set() in libs/testdiff also registers the JSON-encoded form of the value, so that # values with quotes or backslashes are replaced inside JSON output as well. encoded = json.dumps(literal, ensure_ascii=False)[1:-1] if encoded != literal: - entries.append((order, re.escape(encoded), new)) - entries.append((order, re.escape(literal), new)) + entries.append((order, re.escape(encoded), new, False)) + entries.append((order, re.escape(literal), new, False)) # Stable sort: replacements with the same order are applied in the order they were added. entries.sort(key=lambda entry: entry[0]) - return [(old, new) for _, old, new in entries] + return [(old, new, distinct) for _, old, new, distinct in entries] def compile_repls(): """Same as read_repls(), with patterns compiled. Invalid patterns are reported and skipped.""" result = [] - for old, new in read_repls(): + for old, new, distinct in read_repls(): try: - result.append((re.compile(old), new)) + result.append((re.compile(old), new, distinct)) except re.error as e: print(f"Regex error for pattern {old}: {e}", file=sys.stderr) return result def replace_all(patterns, s): - for comp, new in patterns: + r"""Apply replacements with the same per-output numbering as the Go harness. + + >>> distinct = [(re.compile(r"item-\w+"), "[ITEM]", True)] + >>> replace_all(distinct, "item-b item-a item-b") + '[ITEM][0] [ITEM][1] [ITEM][0]' + >>> replace_all(distinct, "item-a item-a") + '[ITEM] [ITEM]' + >>> replace_all(distinct, "no matches") + 'no matches' + >>> replace_all(distinct, "[ITEM][0] [ITEM][1] [ITEM][0]") + '[ITEM][0] [ITEM][1] [ITEM][0]' + >>> replace_all([(re.compile(r"item-\w+"), "[ITEM]", False)], "item-a item-b") + '[ITEM] [ITEM]' + """ + for comp, new, distinct in patterns: + if distinct: + seen = {} + for match in comp.finditer(s): + value = match.group(0) + if value not in seen: + seen[value] = len(seen) + if len(seen) > 1: + s = comp.sub(lambda match, new=new, seen=seen: match.expand(new) + f"[{seen[match.group(0)]}]", s) + continue s = comp.sub(new, s) return s diff --git a/acceptance/bundle/deploy/immutable/no-artifacts/out.plan.txt b/acceptance/bundle/deploy/immutable/no-artifacts/out.plan.txt new file mode 100644 index 00000000000..9bdc7957f1a --- /dev/null +++ b/acceptance/bundle/deploy/immutable/no-artifacts/out.plan.txt @@ -0,0 +1,46 @@ + +>>> [CLI] bundle plan -o json + +>>> jq .plan["resources.jobs.my_job"].new_state.value.tasks plan.json +[ + { + "notebook_task": { + "base_parameters": { + "path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/some_path" + }, + "notebook_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/notebook" + }, + "task_key": "notebook_task" + }, + { + "environment_key": "env", + "spark_python_task": { + "python_file": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/main.py" + }, + "task_key": "spark_python_task" + } +] + +>>> jq .plan["resources.internal_immutable_snapshots.immutable"] plan.json +{ + "action": "create", + "new_state": { + "value": { + "relative_path": "[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]", + "full_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot", + "bundle_id": "[SNAPSHOT_HASH]", + "acl": [ + { + "user_name": "[USERNAME]", + "permission_level": "CAN_READ" + } + ], + "can_manage": [ + { + "service_principal_name": "[USERNAME]" + } + ], + "zip_path": "[TEST_TMP_DIR]/.databricks/bundle/default/snapshots/[SNAPSHOT_HASH]-[UUID].zip" + } + } +} diff --git a/acceptance/bundle/deploy/immutable/no-artifacts/output.txt b/acceptance/bundle/deploy/immutable/no-artifacts/output.txt index 96f778441ac..e18559b02a0 100644 --- a/acceptance/bundle/deploy/immutable/no-artifacts/output.txt +++ b/acceptance/bundle/deploy/immutable/no-artifacts/output.txt @@ -8,52 +8,6 @@ Workspace: Validation OK! ->>> [CLI] bundle plan -o json - ->>> jq .plan["resources.jobs.my_job"].new_state.value.tasks plan.json -[ - { - "notebook_task": { - "base_parameters": { - "path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/some_path" - }, - "notebook_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/notebook" - }, - "task_key": "notebook_task" - }, - { - "environment_key": "env", - "spark_python_task": { - "python_file": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot/files/src/main.py" - }, - "task_key": "spark_python_task" - } -] - ->>> jq .plan["resources.internal_immutable_snapshots.immutable"] plan.json -{ - "action": "create", - "new_state": { - "value": { - "relative_path": "[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]", - "full_path": "/Workspace/Users/[UUID]/.snapshots/[SNAPSHOT_HASH]/[SNAPSHOT_HASH]-[UUID]/snapshot", - "bundle_id": "[SNAPSHOT_HASH]", - "acl": [ - { - "user_name": "[USERNAME]", - "permission_level": "CAN_READ" - } - ], - "can_manage": [ - { - "service_principal_name": "[USERNAME]" - } - ], - "zip_path": "[TEST_TMP_DIR]/.databricks/bundle/default/snapshots/[SNAPSHOT_HASH]-[UUID].zip" - } - } -} - >>> [CLI] bundle deploy Created internal_immutable_snapshots.immutable Created jobs.my_job diff --git a/acceptance/bundle/deploy/immutable/no-artifacts/script b/acceptance/bundle/deploy/immutable/no-artifacts/script index 4bf3fddd7a6..b9ea0ae82d1 100644 --- a/acceptance/bundle/deploy/immutable/no-artifacts/script +++ b/acceptance/bundle/deploy/immutable/no-artifacts/script @@ -1,9 +1,13 @@ envsubst < databricks.yml.tmpl > databricks.yml trace $CLI bundle validate -trace $CLI bundle plan -o json > plan.json -trace jq '.plan["resources.jobs.my_job"].new_state.value.tasks' plan.json -trace jq '.plan["resources.internal_immutable_snapshots.immutable"]' plan.json +# A direct deploy generates a new snapshot UUID, while --plan reuses the planned +# one. Keep the initial plan in its own golden so both variants agree per file. +{ + trace $CLI bundle plan -o json > plan.json + trace jq '.plan["resources.jobs.my_job"].new_state.value.tasks' plan.json + trace jq '.plan["resources.internal_immutable_snapshots.immutable"]' plan.json +} > out.plan.txt 2>&1 trace $CLI bundle deploy $(readplanarg plan.json)