|
1 | 1 | package dev.dbos.transact.database; |
2 | 2 |
|
| 3 | +import static org.junit.jupiter.api.Assertions.assertArrayEquals; |
3 | 4 | import static org.junit.jupiter.api.Assertions.assertEquals; |
4 | 5 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| 6 | +import static org.junit.jupiter.api.Assertions.assertNotEquals; |
5 | 7 | import static org.junit.jupiter.api.Assertions.assertNotNull; |
6 | 8 | import static org.junit.jupiter.api.Assertions.assertNull; |
7 | 9 | import static org.junit.jupiter.api.Assertions.assertThrows; |
8 | 10 | import static org.junit.jupiter.api.Assertions.assertTrue; |
9 | 11 |
|
10 | | -import dev.dbos.transact.DBOS; |
11 | 12 | import dev.dbos.transact.config.DBOSConfig; |
12 | 13 | import dev.dbos.transact.exceptions.DBOSMaxRecoveryAttemptsExceededException; |
13 | 14 | import dev.dbos.transact.exceptions.DBOSQueueDuplicatedException; |
@@ -44,7 +45,6 @@ public class SystemDatabaseTest { |
44 | 45 | DBOSConfig dbosConfig; |
45 | 46 | @AutoClose SystemDatabase sysdb; |
46 | 47 |
|
47 | | - @AutoClose DBOS dbos; |
48 | 48 | @AutoClose HikariDataSource dataSource; |
49 | 49 |
|
50 | 50 | @BeforeEach |
@@ -763,4 +763,290 @@ public void testCreateScheduleWithAllFields() { |
763 | 763 | assertEquals(ZoneId.of("America/New_York"), s.cronTimezone()); |
764 | 764 | assertEquals("my-queue", s.queueName()); |
765 | 765 | } |
| 766 | + |
| 767 | + @Test |
| 768 | + public void testWorkflowStatusAuthenticationFields() throws Exception { |
| 769 | + var workflowId = "test-auth-workflow"; |
| 770 | + var authenticatedUser = "user@example.com"; |
| 771 | + var assumedRole = "admin"; |
| 772 | + var authenticatedRoles = new String[] {"admin", "operator", "viewer"}; |
| 773 | + |
| 774 | + // Create workflow status with authentication fields |
| 775 | + var status = |
| 776 | + WorkflowStatusInternal.builder(workflowId, WorkflowState.PENDING) |
| 777 | + .name("TestWorkflow") |
| 778 | + .className("com.example.TestWorkflow") |
| 779 | + .authenticatedUser(authenticatedUser) |
| 780 | + .assumedRole(assumedRole) |
| 781 | + .authenticatedRoles(authenticatedRoles) |
| 782 | + .build(); |
| 783 | + |
| 784 | + // Insert into database |
| 785 | + sysdb.initWorkflowStatus(status, null, false, false); |
| 786 | + |
| 787 | + // Retrieve via SystemDatabase API and validate object mapping |
| 788 | + var retrievedStatus = sysdb.getWorkflowStatus(workflowId); |
| 789 | + assertNotNull(retrievedStatus); |
| 790 | + assertEquals(authenticatedUser, retrievedStatus.authenticatedUser()); |
| 791 | + assertEquals(assumedRole, retrievedStatus.assumedRole()); |
| 792 | + assertArrayEquals(authenticatedRoles, retrievedStatus.authenticatedRoles()); |
| 793 | + |
| 794 | + // Validate raw database values using DBUtils |
| 795 | + var rawRow = DBUtils.getWorkflowRow(dataSource, workflowId); |
| 796 | + assertNotNull(rawRow); |
| 797 | + assertEquals(authenticatedUser, rawRow.authenticatedUser()); |
| 798 | + assertEquals(assumedRole, rawRow.assumedRole()); |
| 799 | + |
| 800 | + // Verify the authenticated_roles are stored as JSON in the database |
| 801 | + assertEquals("[\"admin\",\"operator\",\"viewer\"]", rawRow.authenticatedRoles()); |
| 802 | + |
| 803 | + // Verify other fields are correctly stored |
| 804 | + assertEquals(workflowId, rawRow.workflowId()); |
| 805 | + assertEquals(WorkflowState.PENDING.name(), rawRow.status()); |
| 806 | + assertEquals("TestWorkflow", rawRow.name()); |
| 807 | + assertEquals("com.example.TestWorkflow", rawRow.className()); |
| 808 | + } |
| 809 | + |
| 810 | + @Test |
| 811 | + public void testWorkflowStatusAuthenticationFieldsWithNulls() throws Exception { |
| 812 | + var workflowId = "test-auth-null-workflow"; |
| 813 | + |
| 814 | + // Create workflow status with null authentication fields |
| 815 | + var status = |
| 816 | + WorkflowStatusInternal.builder(workflowId, WorkflowState.PENDING) |
| 817 | + .name("TestNullAuthWorkflow") |
| 818 | + .className("com.example.TestNullAuthWorkflow") |
| 819 | + .authenticatedUser(null) |
| 820 | + .assumedRole(null) |
| 821 | + .authenticatedRoles(null) |
| 822 | + .build(); |
| 823 | + |
| 824 | + // Insert into database |
| 825 | + sysdb.initWorkflowStatus(status, null, false, false); |
| 826 | + |
| 827 | + // Retrieve via SystemDatabase API and validate null handling |
| 828 | + var retrievedStatus = sysdb.getWorkflowStatus(workflowId); |
| 829 | + assertNotNull(retrievedStatus); |
| 830 | + assertNull(retrievedStatus.authenticatedUser()); |
| 831 | + assertNull(retrievedStatus.assumedRole()); |
| 832 | + assertNull(retrievedStatus.authenticatedRoles()); |
| 833 | + |
| 834 | + // Validate raw database values are null |
| 835 | + var rawRow = DBUtils.getWorkflowRow(dataSource, workflowId); |
| 836 | + assertNotNull(rawRow); |
| 837 | + assertNull(rawRow.authenticatedUser()); |
| 838 | + assertNull(rawRow.assumedRole()); |
| 839 | + assertNull(rawRow.authenticatedRoles()); |
| 840 | + } |
| 841 | + |
| 842 | + @Test |
| 843 | + public void testWorkflowStatusEmptyAuthenticatedRoles() throws Exception { |
| 844 | + var workflowId = "test-auth-empty-roles-workflow"; |
| 845 | + var authenticatedUser = "user@example.com"; |
| 846 | + var assumedRole = "basic"; |
| 847 | + var authenticatedRoles = new String[0]; // Empty array |
| 848 | + |
| 849 | + // Create workflow status with empty authenticated roles |
| 850 | + var status = |
| 851 | + WorkflowStatusInternal.builder(workflowId, WorkflowState.PENDING) |
| 852 | + .name("TestEmptyRolesWorkflow") |
| 853 | + .className("com.example.TestEmptyRolesWorkflow") |
| 854 | + .authenticatedUser(authenticatedUser) |
| 855 | + .assumedRole(assumedRole) |
| 856 | + .authenticatedRoles(authenticatedRoles) |
| 857 | + .build(); |
| 858 | + |
| 859 | + // Insert into database |
| 860 | + sysdb.initWorkflowStatus(status, null, false, false); |
| 861 | + |
| 862 | + // Retrieve via SystemDatabase API and validate empty list handling |
| 863 | + var retrievedStatus = sysdb.getWorkflowStatus(workflowId); |
| 864 | + assertNotNull(retrievedStatus); |
| 865 | + assertEquals(authenticatedUser, retrievedStatus.authenticatedUser()); |
| 866 | + assertEquals(assumedRole, retrievedStatus.assumedRole()); |
| 867 | + assertEquals(0, retrievedStatus.authenticatedRoles().length); |
| 868 | + |
| 869 | + // Validate raw database values |
| 870 | + var rawRow = DBUtils.getWorkflowRow(dataSource, workflowId); |
| 871 | + assertNotNull(rawRow); |
| 872 | + assertEquals(authenticatedUser, rawRow.authenticatedUser()); |
| 873 | + assertEquals(assumedRole, rawRow.assumedRole()); |
| 874 | + assertEquals("[]", rawRow.authenticatedRoles()); // Empty JSON array |
| 875 | + } |
| 876 | + |
| 877 | + @Test |
| 878 | + public void testForkWorkflowWithAuthenticationFields() throws Exception { |
| 879 | + var originalWorkflowId = "test-fork-original-workflow"; |
| 880 | + var authenticatedUser = "user@example.com"; |
| 881 | + var assumedRole = "admin"; |
| 882 | + var authenticatedRoles = new String[] {"admin", "operator", "viewer"}; |
| 883 | + |
| 884 | + // Create original workflow status with authentication fields |
| 885 | + var originalStatus = |
| 886 | + WorkflowStatusInternal.builder(originalWorkflowId, WorkflowState.SUCCESS) |
| 887 | + .name("OriginalTestWorkflow") |
| 888 | + .className("com.example.OriginalTestWorkflow") |
| 889 | + .authenticatedUser(authenticatedUser) |
| 890 | + .assumedRole(assumedRole) |
| 891 | + .authenticatedRoles(authenticatedRoles) |
| 892 | + .build(); |
| 893 | + |
| 894 | + // Insert original workflow into database |
| 895 | + sysdb.initWorkflowStatus(originalStatus, null, false, false); |
| 896 | + |
| 897 | + // Verify original workflow has correct authentication fields |
| 898 | + var originalRetrieved = sysdb.getWorkflowStatus(originalWorkflowId); |
| 899 | + assertNotNull(originalRetrieved); |
| 900 | + assertEquals(authenticatedUser, originalRetrieved.authenticatedUser()); |
| 901 | + assertEquals(assumedRole, originalRetrieved.assumedRole()); |
| 902 | + assertArrayEquals(authenticatedRoles, originalRetrieved.authenticatedRoles()); |
| 903 | + |
| 904 | + // Fork the workflow |
| 905 | + var forkOptions = new dev.dbos.transact.workflow.ForkOptions(null, "1.0.0", null); |
| 906 | + var forkedWorkflowId = sysdb.forkWorkflow(originalWorkflowId, 0, forkOptions); |
| 907 | + assertNotNull(forkedWorkflowId); |
| 908 | + assertNotEquals(originalWorkflowId, forkedWorkflowId); |
| 909 | + |
| 910 | + // Retrieve forked workflow and validate authentication fields are copied |
| 911 | + var forkedStatus = sysdb.getWorkflowStatus(forkedWorkflowId); |
| 912 | + assertNotNull(forkedStatus); |
| 913 | + assertEquals(authenticatedUser, forkedStatus.authenticatedUser()); |
| 914 | + assertEquals(assumedRole, forkedStatus.assumedRole()); |
| 915 | + assertArrayEquals(authenticatedRoles, forkedStatus.authenticatedRoles()); |
| 916 | + |
| 917 | + // Verify other forked workflow properties |
| 918 | + assertEquals("OriginalTestWorkflow", forkedStatus.name()); |
| 919 | + assertEquals("com.example.OriginalTestWorkflow", forkedStatus.className()); |
| 920 | + assertEquals( |
| 921 | + WorkflowState.ENQUEUED.name(), forkedStatus.status()); // Forked workflows start as ENQUEUED |
| 922 | + assertEquals(originalWorkflowId, forkedStatus.forkedFrom()); |
| 923 | + |
| 924 | + // Validate raw database values for both workflows |
| 925 | + var originalRawRow = DBUtils.getWorkflowRow(dataSource, originalWorkflowId); |
| 926 | + var forkedRawRow = DBUtils.getWorkflowRow(dataSource, forkedWorkflowId); |
| 927 | + |
| 928 | + assertNotNull(originalRawRow); |
| 929 | + assertNotNull(forkedRawRow); |
| 930 | + |
| 931 | + // Authentication fields should be identical in raw DB |
| 932 | + assertEquals(originalRawRow.authenticatedUser(), forkedRawRow.authenticatedUser()); |
| 933 | + assertEquals(originalRawRow.assumedRole(), forkedRawRow.assumedRole()); |
| 934 | + assertEquals(originalRawRow.authenticatedRoles(), forkedRawRow.authenticatedRoles()); |
| 935 | + assertEquals("[\"admin\",\"operator\",\"viewer\"]", forkedRawRow.authenticatedRoles()); |
| 936 | + |
| 937 | + // Verify forked_from field is set correctly |
| 938 | + assertNull(originalRawRow.forkedFrom()); |
| 939 | + assertEquals(originalWorkflowId, forkedRawRow.forkedFrom()); |
| 940 | + } |
| 941 | + |
| 942 | + @Test |
| 943 | + public void testForkWorkflowWithNullAuthenticationFields() throws Exception { |
| 944 | + var originalWorkflowId = "test-fork-null-auth-workflow"; |
| 945 | + |
| 946 | + // Create original workflow status with null authentication fields |
| 947 | + var originalStatus = |
| 948 | + WorkflowStatusInternal.builder(originalWorkflowId, WorkflowState.SUCCESS) |
| 949 | + .name("NullAuthTestWorkflow") |
| 950 | + .className("com.example.NullAuthTestWorkflow") |
| 951 | + .authenticatedUser(null) |
| 952 | + .assumedRole(null) |
| 953 | + .authenticatedRoles(null) |
| 954 | + .build(); |
| 955 | + |
| 956 | + // Insert original workflow into database |
| 957 | + sysdb.initWorkflowStatus(originalStatus, null, false, false); |
| 958 | + |
| 959 | + // Fork the workflow |
| 960 | + var forkOptions = new dev.dbos.transact.workflow.ForkOptions(null, "1.0.0", null); |
| 961 | + var forkedWorkflowId = sysdb.forkWorkflow(originalWorkflowId, 0, forkOptions); |
| 962 | + assertNotNull(forkedWorkflowId); |
| 963 | + |
| 964 | + // Retrieve forked workflow and validate null authentication fields are preserved |
| 965 | + var forkedStatus = sysdb.getWorkflowStatus(forkedWorkflowId); |
| 966 | + assertNotNull(forkedStatus); |
| 967 | + assertNull(forkedStatus.authenticatedUser()); |
| 968 | + assertNull(forkedStatus.assumedRole()); |
| 969 | + assertNull(forkedStatus.authenticatedRoles()); |
| 970 | + |
| 971 | + // Validate raw database values |
| 972 | + var originalRawRow = DBUtils.getWorkflowRow(dataSource, originalWorkflowId); |
| 973 | + var forkedRawRow = DBUtils.getWorkflowRow(dataSource, forkedWorkflowId); |
| 974 | + |
| 975 | + assertNotNull(originalRawRow); |
| 976 | + assertNotNull(forkedRawRow); |
| 977 | + |
| 978 | + // Null authentication fields should be preserved |
| 979 | + assertNull(originalRawRow.authenticatedUser()); |
| 980 | + assertNull(forkedRawRow.authenticatedUser()); |
| 981 | + assertNull(originalRawRow.assumedRole()); |
| 982 | + assertNull(forkedRawRow.assumedRole()); |
| 983 | + assertNull(originalRawRow.authenticatedRoles()); |
| 984 | + assertNull(forkedRawRow.authenticatedRoles()); |
| 985 | + } |
| 986 | + |
| 987 | + @Test |
| 988 | + public void testForkWorkflowWithEmptyAuthenticatedRoles() throws Exception { |
| 989 | + var originalWorkflowId = "test-fork-empty-auth-roles-workflow"; |
| 990 | + var authenticatedUser = "user@example.com"; |
| 991 | + var assumedRole = "basic"; |
| 992 | + var authenticatedRoles = new String[0]; // Empty array |
| 993 | + |
| 994 | + // Create original workflow status with empty authenticated roles |
| 995 | + var originalStatus = |
| 996 | + WorkflowStatusInternal.builder(originalWorkflowId, WorkflowState.SUCCESS) |
| 997 | + .name("EmptyAuthRolesTestWorkflow") |
| 998 | + .className("com.example.EmptyAuthRolesTestWorkflow") |
| 999 | + .authenticatedUser(authenticatedUser) |
| 1000 | + .assumedRole(assumedRole) |
| 1001 | + .authenticatedRoles(authenticatedRoles) |
| 1002 | + .build(); |
| 1003 | + |
| 1004 | + // Insert original workflow into database |
| 1005 | + sysdb.initWorkflowStatus(originalStatus, null, false, false); |
| 1006 | + |
| 1007 | + // Verify original workflow has correct authentication fields including empty roles |
| 1008 | + var originalRetrieved = sysdb.getWorkflowStatus(originalWorkflowId); |
| 1009 | + assertNotNull(originalRetrieved); |
| 1010 | + assertEquals(authenticatedUser, originalRetrieved.authenticatedUser()); |
| 1011 | + assertEquals(assumedRole, originalRetrieved.assumedRole()); |
| 1012 | + assertEquals(0, originalRetrieved.authenticatedRoles().length); |
| 1013 | + |
| 1014 | + // Fork the workflow |
| 1015 | + var forkOptions = new dev.dbos.transact.workflow.ForkOptions(null, "1.0.0", null); |
| 1016 | + var forkedWorkflowId = sysdb.forkWorkflow(originalWorkflowId, 0, forkOptions); |
| 1017 | + assertNotNull(forkedWorkflowId); |
| 1018 | + assertNotEquals(originalWorkflowId, forkedWorkflowId); |
| 1019 | + |
| 1020 | + // Retrieve forked workflow and validate empty authentication roles are preserved |
| 1021 | + var forkedStatus = sysdb.getWorkflowStatus(forkedWorkflowId); |
| 1022 | + assertNotNull(forkedStatus); |
| 1023 | + assertEquals(authenticatedUser, forkedStatus.authenticatedUser()); |
| 1024 | + assertEquals(assumedRole, forkedStatus.assumedRole()); |
| 1025 | + assertEquals(0, forkedStatus.authenticatedRoles().length); |
| 1026 | + |
| 1027 | + // Verify other forked workflow properties |
| 1028 | + assertEquals("EmptyAuthRolesTestWorkflow", forkedStatus.name()); |
| 1029 | + assertEquals("com.example.EmptyAuthRolesTestWorkflow", forkedStatus.className()); |
| 1030 | + assertEquals(WorkflowState.ENQUEUED.name(), forkedStatus.status()); |
| 1031 | + assertEquals(originalWorkflowId, forkedStatus.forkedFrom()); |
| 1032 | + |
| 1033 | + // Validate raw database values for both workflows |
| 1034 | + var originalRawRow = DBUtils.getWorkflowRow(dataSource, originalWorkflowId); |
| 1035 | + var forkedRawRow = DBUtils.getWorkflowRow(dataSource, forkedWorkflowId); |
| 1036 | + |
| 1037 | + assertNotNull(originalRawRow); |
| 1038 | + assertNotNull(forkedRawRow); |
| 1039 | + |
| 1040 | + // Empty authentication roles should be stored as empty JSON array and preserved in fork |
| 1041 | + assertEquals(authenticatedUser, originalRawRow.authenticatedUser()); |
| 1042 | + assertEquals(authenticatedUser, forkedRawRow.authenticatedUser()); |
| 1043 | + assertEquals(assumedRole, originalRawRow.assumedRole()); |
| 1044 | + assertEquals(assumedRole, forkedRawRow.assumedRole()); |
| 1045 | + assertEquals("[]", originalRawRow.authenticatedRoles()); // Empty JSON array |
| 1046 | + assertEquals("[]", forkedRawRow.authenticatedRoles()); // Empty JSON array preserved in fork |
| 1047 | + |
| 1048 | + // Verify forked_from field is set correctly |
| 1049 | + assertNull(originalRawRow.forkedFrom()); |
| 1050 | + assertEquals(originalWorkflowId, forkedRawRow.forkedFrom()); |
| 1051 | + } |
766 | 1052 | } |
0 commit comments