1+ from unittest .mock import patch
2+
13from django .shortcuts import reverse
4+ from django .test .utils import override_settings
25
36import pytest
47from pytest_unordered import unordered
710from baserow .contrib .database .fields .dependencies .models import FieldDependency
811from baserow .contrib .database .fields .handler import FieldHandler
912from baserow .contrib .database .fields .registries import field_type_registry
13+ from baserow .contrib .database .fields .utils .deferred_foreign_key_updater import (
14+ DeferredForeignKeyUpdater ,
15+ )
1016from baserow .contrib .database .rows .handler import RowHandler
1117from baserow .contrib .database .table .handler import TableHandler
1218from baserow .core .cache import local_cache
1319from baserow .core .db import specific_iterator
20+ from baserow .core .registries import ImportExportConfig
1421from baserow_premium .fields .field_types import AIFieldType
1522from baserow_premium .fields .models import AIField
1623
@@ -710,11 +717,16 @@ def test_update_ai_field_type_via_api_file_field_doesnt_exist(
710717 assert response_json ["error" ] == "ERROR_FIELD_DOES_NOT_EXIST"
711718
712719
713- @pytest .mark .django_db
720+ @pytest .mark .django_db ( transaction = True )
714721@pytest .mark .field_ai
715- def test_duplicate_table_with_ai_field (premium_data_fixture ):
722+ @override_settings (DEBUG = True )
723+ @patch ("baserow.core.jobs.handler.JobHandler.create_and_start_job" )
724+ def test_duplicate_table_with_ai_field (patched_job_creation , premium_data_fixture ):
725+ premium_data_fixture .register_fake_generate_ai_type ()
716726 session_id = "session-id"
717- user = premium_data_fixture .create_user (session_id = session_id )
727+ user = premium_data_fixture .create_user (
728+ session_id = session_id , has_active_premium_license = True
729+ )
718730 database = premium_data_fixture .create_database_application (
719731 user = user , name = "Placeholder"
720732 )
@@ -735,6 +747,8 @@ def test_duplicate_table_with_ai_field(premium_data_fixture):
735747 ai_generative_ai_model = "test_1" ,
736748 ai_file_field = file_field ,
737749 ai_prompt = f"concat('test:',get('fields.field_{ text_field .id } '))" ,
750+ ai_auto_update = True ,
751+ ai_auto_update_user = user ,
738752 )
739753
740754 table_handler = TableHandler ()
@@ -754,6 +768,20 @@ def test_duplicate_table_with_ai_field(premium_data_fixture):
754768 duplicated_ai_field .ai_prompt ["formula" ]
755769 == f"concat('test:',get('fields.field_{ duplicated_text_field .id } '))"
756770 )
771+ assert duplicated_ai_field .ai_auto_update is True
772+ assert duplicated_ai_field .ai_auto_update_user_id == user .id
773+
774+ # Verify auto-update triggers on the duplicated table's AI field.
775+ patched_job_creation .reset_mock ()
776+ RowHandler ().create_rows (
777+ user ,
778+ duplicated_table ,
779+ rows_values = [{duplicated_text_field .db_column : "test" }],
780+ send_webhook_events = False ,
781+ send_realtime_update = False ,
782+ )
783+ assert patched_job_creation .call_count == 1
784+ assert patched_job_creation .call_args .kwargs ["field_id" ] == duplicated_ai_field .id
757785
758786
759787@pytest .mark .django_db
@@ -780,7 +808,7 @@ def test_duplicate_table_with_ai_field_broken_references(premium_data_fixture):
780808 ai_generative_ai_type = "test_generative_ai" ,
781809 ai_generative_ai_model = "test_1" ,
782810 ai_file_field = file_field ,
783- ai_prompt = f "concat('test:',get('fields.field_0'))" ,
811+ ai_prompt = "concat('test:',get('fields.field_0'))" ,
784812 )
785813
786814 table_handler = TableHandler ()
@@ -792,7 +820,7 @@ def test_duplicate_table_with_ai_field_broken_references(premium_data_fixture):
792820
793821 assert (
794822 duplicated_ai_field .ai_prompt ["formula" ]
795- == f "concat('test:',get('fields.field_0'))"
823+ == "concat('test:',get('fields.field_0'))"
796824 )
797825
798826
@@ -1352,3 +1380,142 @@ def test_create_ai_field_auto_doesnt_update_user_if_set(premium_data_fixture):
13521380
13531381 assert ai_field .ai_auto_update is True
13541382 assert ai_field .ai_auto_update_user_id == user .id # not changed
1383+
1384+
1385+ @pytest .mark .django_db
1386+ @pytest .mark .field_ai
1387+ def test_import_serialized_ai_field_with_auto_update_user (premium_data_fixture ):
1388+ user = premium_data_fixture .create_user ()
1389+ table = premium_data_fixture .create_database_table (user = user )
1390+ premium_data_fixture .register_fake_generate_ai_type ()
1391+ text_field = premium_data_fixture .create_text_field (
1392+ table = table , order = 0 , name = "text"
1393+ )
1394+
1395+ ai_field = premium_data_fixture .create_ai_field (
1396+ table = table ,
1397+ order = 1 ,
1398+ name = "ai" ,
1399+ ai_generative_ai_type = "test_generative_ai" ,
1400+ ai_generative_ai_model = "test_1" ,
1401+ ai_prompt = f"concat('test:',get('fields.field_{ text_field .id } '))" ,
1402+ ai_auto_update = True ,
1403+ ai_auto_update_user = user ,
1404+ )
1405+
1406+ field_type = field_type_registry .get_by_model (ai_field )
1407+ serialized = field_type .export_serialized (ai_field )
1408+
1409+ serialized ["ai_auto_update_user_id" ] = 99999
1410+
1411+ imported_field = field_type .import_serialized (
1412+ table ,
1413+ serialized ,
1414+ ImportExportConfig (include_permission_data = False ),
1415+ id_mapping = {},
1416+ deferred_fk_update_collector = DeferredForeignKeyUpdater (),
1417+ )
1418+
1419+ imported_field = AIField .objects .get (id = imported_field .id )
1420+ assert imported_field .ai_auto_update is False
1421+ assert imported_field .ai_auto_update_user_id is None
1422+
1423+
1424+ @pytest .mark .django_db (transaction = True )
1425+ @pytest .mark .field_ai
1426+ @override_settings (DEBUG = True )
1427+ @patch ("baserow.core.jobs.handler.JobHandler.create_and_start_job" )
1428+ def test_duplicate_field_with_ai_auto_update_triggers_both (
1429+ patched_job_creation , premium_data_fixture
1430+ ):
1431+ premium_data_fixture .register_fake_generate_ai_type ()
1432+ user = premium_data_fixture .create_user (has_active_premium_license = True )
1433+ database = premium_data_fixture .create_database_application (
1434+ user = user , name = "database"
1435+ )
1436+ table = premium_data_fixture .create_database_table (name = "table" , database = database )
1437+ text_field = premium_data_fixture .create_text_field (table = table , name = "text" )
1438+ ai_field = FieldHandler ().create_field (
1439+ table = table ,
1440+ user = user ,
1441+ name = "ai" ,
1442+ type_name = "ai" ,
1443+ ai_generative_ai_type = "test_generative_ai" ,
1444+ ai_generative_ai_model = "test_1" ,
1445+ ai_prompt = f"get('fields.field_{ text_field .id } ')" ,
1446+ ai_auto_update = True ,
1447+ )
1448+
1449+ assert ai_field .ai_auto_update is True
1450+ assert ai_field .ai_auto_update_user_id == user .id
1451+
1452+ RowHandler ().create_rows (
1453+ user ,
1454+ table ,
1455+ rows_values = [{text_field .db_column : "test" }],
1456+ send_webhook_events = False ,
1457+ send_realtime_update = False ,
1458+ )
1459+ assert patched_job_creation .call_count == 1
1460+ assert patched_job_creation .call_args .kwargs ["field_id" ] == ai_field .id
1461+
1462+ duplicated_field , _ = FieldHandler ().duplicate_field (user , ai_field )
1463+ duplicated_field = duplicated_field .specific
1464+
1465+ assert duplicated_field .ai_auto_update is True
1466+ assert duplicated_field .ai_auto_update_user_id == user .id
1467+
1468+ patched_job_creation .reset_mock ()
1469+ RowHandler ().create_rows (
1470+ user ,
1471+ table ,
1472+ rows_values = [{text_field .db_column : "test2" }],
1473+ send_webhook_events = False ,
1474+ send_realtime_update = False ,
1475+ )
1476+ assert patched_job_creation .call_count == 2
1477+ triggered_field_ids = {
1478+ call .kwargs ["field_id" ] for call in patched_job_creation .call_args_list
1479+ }
1480+ assert triggered_field_ids == {ai_field .id , duplicated_field .id }
1481+
1482+
1483+ @pytest .mark .django_db
1484+ @pytest .mark .field_ai
1485+ def test_import_ai_field_disables_auto_update (premium_data_fixture ):
1486+ premium_data_fixture .register_fake_generate_ai_type ()
1487+ user = premium_data_fixture .create_user ()
1488+ database = premium_data_fixture .create_database_application (
1489+ user = user , name = "database"
1490+ )
1491+ table = premium_data_fixture .create_database_table (name = "table" , database = database )
1492+ text_field = premium_data_fixture .create_text_field (table = table , name = "text" )
1493+ ai_field = FieldHandler ().create_field (
1494+ table = table ,
1495+ user = user ,
1496+ name = "ai" ,
1497+ type_name = "ai" ,
1498+ ai_generative_ai_type = "test_generative_ai" ,
1499+ ai_generative_ai_model = "test_1" ,
1500+ ai_prompt = f"get('fields.field_{ text_field .id } ')" ,
1501+ ai_auto_update = True ,
1502+ )
1503+
1504+ assert ai_field .ai_auto_update_user_id == user .id
1505+
1506+ field_type = field_type_registry .get_by_model (ai_field )
1507+ serialized = field_type .export_serialized (ai_field )
1508+
1509+ serialized ["ai_auto_update_user_id" ] = 99999
1510+
1511+ imported_field = field_type .import_serialized (
1512+ table ,
1513+ serialized ,
1514+ ImportExportConfig (include_permission_data = False ),
1515+ id_mapping = {},
1516+ deferred_fk_update_collector = DeferredForeignKeyUpdater (),
1517+ )
1518+
1519+ imported_field = AIField .objects .get (id = imported_field .id )
1520+ assert imported_field .ai_auto_update is False
1521+ assert imported_field .ai_auto_update_user_id is None
0 commit comments