|
20 | 20 | use ProcessMaker\Models\ProcessTaskAssignment; |
21 | 21 | use ProcessMaker\Models\Screen; |
22 | 22 | use ProcessMaker\Models\ScreenVersion; |
| 23 | +use ProcessMaker\Models\Setting; |
23 | 24 | use ProcessMaker\Models\User; |
24 | 25 | use ProcessMaker\Notifications\ActivityActivatedNotification; |
25 | 26 | use ProcessMaker\Providers\AuthServiceProvider; |
@@ -913,4 +914,115 @@ public function testGetAllTaskTypes() |
913 | 914 | $this->assertEquals($scriptTask->id, $response['data'][1]['id']); |
914 | 915 | $this->assertEquals($gatewayTask->id, $response['data'][2]['id']); |
915 | 916 | } |
| 917 | + |
| 918 | + /** |
| 919 | + * Test that the free-text filter parameter works on the tasks endpoint. |
| 920 | + */ |
| 921 | + public function testFilterByFreeTextSearch() |
| 922 | + { |
| 923 | + $request = ProcessRequest::factory()->create(); |
| 924 | + |
| 925 | + $matchingTask = ProcessRequestToken::factory()->create([ |
| 926 | + 'element_type' => 'task', |
| 927 | + 'element_name' => 'UniqueSearchableTaskName', |
| 928 | + 'status' => 'ACTIVE', |
| 929 | + 'process_request_id' => $request->id, |
| 930 | + ]); |
| 931 | + |
| 932 | + ProcessRequestToken::factory()->create([ |
| 933 | + 'element_type' => 'task', |
| 934 | + 'element_name' => 'OtherTask', |
| 935 | + 'status' => 'ACTIVE', |
| 936 | + 'process_request_id' => $request->id, |
| 937 | + ]); |
| 938 | + |
| 939 | + $response = $this->apiCall('GET', route('api.' . $this->resource . '.index', [ |
| 940 | + 'filter' => 'UniqueSearchableTaskName', |
| 941 | + 'per_page' => 50, |
| 942 | + ])); |
| 943 | + |
| 944 | + $response->assertStatus(200); |
| 945 | + $data = $response->json('data'); |
| 946 | + $this->assertNotEmpty($data); |
| 947 | + $this->assertTrue( |
| 948 | + collect($data)->contains('id', $matchingTask->id), |
| 949 | + 'Expected matching task to appear in filtered results' |
| 950 | + ); |
| 951 | + $this->assertFalse( |
| 952 | + collect($data)->contains('element_name', 'OtherTask'), |
| 953 | + 'Expected non-matching task to be excluded from filtered results' |
| 954 | + ); |
| 955 | + } |
| 956 | + |
| 957 | + /** |
| 958 | + * Build a query that joins process_request_tokens to itself via a |
| 959 | + * subquery aliased `cte`, matching the pattern used by the Saved Search |
| 960 | + * package. Any WHERE clauses added afterwards must use qualified column |
| 961 | + * names or they will hit an "ambiguous column" error. |
| 962 | + */ |
| 963 | + private function buildJoinedSubquery() |
| 964 | + { |
| 965 | + $query = ProcessRequestToken::query()->where('element_type', 'task'); |
| 966 | + |
| 967 | + $subQuery = clone $query; |
| 968 | + $subQuery->select('id', 'updated_at')->latest('updated_at')->take(25); |
| 969 | + |
| 970 | + $query->select('process_request_tokens.*') |
| 971 | + ->joinSub($subQuery, 'cte', function ($join) { |
| 972 | + $join->on('process_request_tokens.id', '=', 'cte.id'); |
| 973 | + }); |
| 974 | + |
| 975 | + return $query; |
| 976 | + } |
| 977 | + |
| 978 | + /** |
| 979 | + * Test that scopeFilter's non-indexed branch works when the query has |
| 980 | + * a join that introduces duplicate column names (Saved Search pattern). |
| 981 | + */ |
| 982 | + public function testScopeFilterWorksWithJoinedSubquery() |
| 983 | + { |
| 984 | + $request = ProcessRequest::factory()->create(); |
| 985 | + |
| 986 | + ProcessRequestToken::factory()->create([ |
| 987 | + 'element_type' => 'task', |
| 988 | + 'element_name' => 'JoinTestTask', |
| 989 | + 'status' => 'ACTIVE', |
| 990 | + 'process_request_id' => $request->id, |
| 991 | + ]); |
| 992 | + |
| 993 | + $query = $this->buildJoinedSubquery(); |
| 994 | + $query->filter('JoinTestTask'); |
| 995 | + |
| 996 | + $results = $query->get(); |
| 997 | + |
| 998 | + $this->assertNotEmpty($results); |
| 999 | + $this->assertTrue($results->contains('element_name', 'JoinTestTask')); |
| 1000 | + } |
| 1001 | + |
| 1002 | + /** |
| 1003 | + * Test that scopeFilter's indexed-search branch (numeric filter path) |
| 1004 | + * uses a qualified `id` column so it works with the Saved Search join. |
| 1005 | + */ |
| 1006 | + public function testScopeFilterIndexedSearchNumericWorksWithJoinedSubquery() |
| 1007 | + { |
| 1008 | + Setting::updateOrCreate( |
| 1009 | + ['key' => 'indexed-search'], |
| 1010 | + ['config' => ['enabled' => true]] |
| 1011 | + ); |
| 1012 | + |
| 1013 | + $request = ProcessRequest::factory()->create(); |
| 1014 | + |
| 1015 | + $task = ProcessRequestToken::factory()->create([ |
| 1016 | + 'element_type' => 'task', |
| 1017 | + 'status' => 'ACTIVE', |
| 1018 | + 'process_request_id' => $request->id, |
| 1019 | + ]); |
| 1020 | + |
| 1021 | + $query = $this->buildJoinedSubquery(); |
| 1022 | + $query->filter((string) $task->id); |
| 1023 | + |
| 1024 | + $results = $query->get(); |
| 1025 | + |
| 1026 | + $this->assertTrue($results->contains('id', $task->id)); |
| 1027 | + } |
916 | 1028 | } |
0 commit comments