@@ -1188,18 +1188,16 @@ async def test_schedule_create_limited_actions_validation(
1188
1188
assert "are remaining actions set" in str (err .value )
1189
1189
1190
1190
1191
- async def test_schedule_search_attribute_update (
1191
+ async def test_schedule_workflow_search_attribute_update (
1192
1192
client : Client , env : WorkflowEnvironment
1193
1193
):
1194
1194
if env .supports_time_skipping :
1195
1195
pytest .skip ("Java test server doesn't support schedules" )
1196
1196
await assert_no_schedules (client )
1197
1197
1198
1198
# Put search attribute on server
1199
- text_attr_key = SearchAttributeKey .for_text (f"python-test-schedule-text" )
1200
- untyped_keyword_key = SearchAttributeKey .for_keyword (
1201
- f"python-test-schedule-keyword"
1202
- )
1199
+ text_attr_key = SearchAttributeKey .for_text ("python-test-schedule-text" )
1200
+ untyped_keyword_key = SearchAttributeKey .for_keyword ("python-test-schedule-keyword" )
1203
1201
await ensure_search_attributes_present (client , text_attr_key , untyped_keyword_key )
1204
1202
1205
1203
# Create a schedule with search attributes on the schedule and on the
@@ -1273,6 +1271,7 @@ def update_schedule_typed_attrs(
1273
1271
# Check that it changed
1274
1272
desc = await handle .describe ()
1275
1273
assert isinstance (desc .schedule .action , ScheduleActionStartWorkflow )
1274
+ # Check that the workflow search attributes were changed
1276
1275
# This assertion has changed since server 1.24. Now, even untyped search
1277
1276
# attributes are given a type server side
1278
1277
assert (
@@ -1283,6 +1282,148 @@ def update_schedule_typed_attrs(
1283
1282
and desc .schedule .action .typed_search_attributes [untyped_keyword_key ]
1284
1283
== "some-untyped-attr1"
1285
1284
)
1285
+ # Check that the schedule search attributes were not changed
1286
+ assert desc .search_attributes [text_attr_key .name ] == ["some-schedule-attr1" ]
1287
+ assert desc .typed_search_attributes [text_attr_key ] == "some-schedule-attr1"
1288
+
1289
+ await handle .delete ()
1290
+ await assert_no_schedules (client )
1291
+
1292
+
1293
+ @pytest .mark .parametrize (
1294
+ "test_case" ,
1295
+ [
1296
+ "none-is-noop" ,
1297
+ "empty-but-non-none-clears" ,
1298
+ "all-new-values-overwrites" ,
1299
+ "partial-new-values-overwrites-and-drops" ,
1300
+ ],
1301
+ )
1302
+ async def test_schedule_search_attribute_update (
1303
+ client : Client , env : WorkflowEnvironment , test_case : str
1304
+ ):
1305
+ if env .supports_time_skipping :
1306
+ pytest .skip ("Java test server doesn't support schedules" )
1307
+ await assert_no_schedules (client )
1308
+
1309
+ # Put search attributes on server
1310
+ key_1 = SearchAttributeKey .for_text ("python-test-schedule-sa-update-key-1" )
1311
+ key_2 = SearchAttributeKey .for_keyword ("python-test-schedule-sa-update-key-2" )
1312
+ await ensure_search_attributes_present (client , key_1 , key_2 )
1313
+ val_1 = "val-1"
1314
+ val_2 = "val-2"
1315
+
1316
+ # Create a schedule with search attributes
1317
+ create_action = ScheduleActionStartWorkflow (
1318
+ "some workflow" ,
1319
+ [],
1320
+ id = f"workflow-{ uuid .uuid4 ()} " ,
1321
+ task_queue = f"tq-{ uuid .uuid4 ()} " ,
1322
+ )
1323
+ handle = await client .create_schedule (
1324
+ f"schedule-{ uuid .uuid4 ()} " ,
1325
+ Schedule (action = create_action , spec = ScheduleSpec ()),
1326
+ search_attributes = TypedSearchAttributes (
1327
+ [
1328
+ SearchAttributePair (key_1 , val_1 ),
1329
+ SearchAttributePair (key_2 , val_2 ),
1330
+ ]
1331
+ ),
1332
+ )
1333
+
1334
+ def update_search_attributes (
1335
+ input : ScheduleUpdateInput ,
1336
+ ) -> Optional [ScheduleUpdate ]:
1337
+ # Make sure the initial search attributes are present
1338
+ assert input .description .search_attributes [key_1 .name ] == [val_1 ]
1339
+ assert input .description .search_attributes [key_2 .name ] == [val_2 ]
1340
+ assert input .description .typed_search_attributes [key_1 ] == val_1
1341
+ assert input .description .typed_search_attributes [key_2 ] == val_2
1342
+
1343
+ if test_case == "none-is-noop" :
1344
+ # Passing None makes no changes
1345
+ return ScheduleUpdate (input .description .schedule , search_attributes = None )
1346
+ elif test_case == "empty-but-non-none-clears" :
1347
+ # Pass empty but non-None to clear all attributes
1348
+ return ScheduleUpdate (
1349
+ input .description .schedule ,
1350
+ search_attributes = TypedSearchAttributes .empty ,
1351
+ )
1352
+ elif test_case == "all-new-values-overwrites" :
1353
+ # Pass all new values to overwrite existing
1354
+ return ScheduleUpdate (
1355
+ input .description .schedule ,
1356
+ search_attributes = input .description .typed_search_attributes .updated (
1357
+ SearchAttributePair (key_1 , val_1 + "-new" ),
1358
+ SearchAttributePair (key_2 , val_2 + "-new" ),
1359
+ ),
1360
+ )
1361
+ elif test_case == "partial-new-values-overwrites-and-drops" :
1362
+ # Only update key_1, which should drop key_2
1363
+ return ScheduleUpdate (
1364
+ input .description .schedule ,
1365
+ search_attributes = TypedSearchAttributes (
1366
+ [
1367
+ SearchAttributePair (key_1 , val_1 + "-new" ),
1368
+ ]
1369
+ ),
1370
+ )
1371
+ else :
1372
+ raise ValueError (f"Invalid test case: { test_case } " )
1373
+
1374
+ await handle .update (update_search_attributes )
1375
+
1376
+ if test_case == "none-is-noop" :
1377
+
1378
+ async def expectation () -> bool :
1379
+ desc = await handle .describe ()
1380
+ return (
1381
+ desc .search_attributes [key_1 .name ] == [val_1 ]
1382
+ and desc .search_attributes [key_2 .name ] == [val_2 ]
1383
+ and desc .typed_search_attributes [key_1 ] == val_1
1384
+ and desc .typed_search_attributes [key_2 ] == val_2
1385
+ )
1386
+
1387
+ await assert_eq_eventually (True , expectation )
1388
+ elif test_case == "empty-but-non-none-clears" :
1389
+
1390
+ async def expectation () -> bool :
1391
+ desc = await handle .describe ()
1392
+ return (
1393
+ len (desc .typed_search_attributes ) == 0
1394
+ and len (desc .search_attributes ) == 0
1395
+ )
1396
+
1397
+ await assert_eq_eventually (True , expectation )
1398
+ elif test_case == "all-new-values-overwrites" :
1399
+
1400
+ async def expectation () -> bool :
1401
+ desc = await handle .describe ()
1402
+ return (
1403
+ desc .search_attributes [key_1 .name ] == [val_1 + "-new" ]
1404
+ and desc .search_attributes [key_2 .name ] == [val_2 + "-new" ]
1405
+ and desc .typed_search_attributes [key_1 ] == val_1 + "-new"
1406
+ and desc .typed_search_attributes [key_2 ] == val_2 + "-new"
1407
+ )
1408
+
1409
+ await assert_eq_eventually (True , expectation )
1410
+ elif test_case == "partial-new-values-overwrites-and-drops" :
1411
+
1412
+ async def expectation () -> bool :
1413
+ desc = await handle .describe ()
1414
+ return (
1415
+ desc .search_attributes [key_1 .name ] == [val_1 + "-new" ]
1416
+ and desc .typed_search_attributes [key_1 ] == val_1 + "-new"
1417
+ and key_2 .name not in desc .search_attributes
1418
+ and key_2 not in desc .typed_search_attributes
1419
+ )
1420
+
1421
+ await assert_eq_eventually (True , expectation )
1422
+ else :
1423
+ raise ValueError (f"Invalid test case: { test_case } " )
1424
+
1425
+ await handle .delete ()
1426
+ await assert_no_schedules (client )
1286
1427
1287
1428
1288
1429
async def assert_no_schedules (client : Client ) -> None :
0 commit comments