1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
7 class Arvados::V1::FiltersTest < ActionController::TestCase
8 test '"not in" filter passes null values' do
9 @controller = Arvados::V1::ContainerRequestsController.new
12 filters: [ ['container_uuid', 'not in', ['zzzzz-dz642-queuedcontainer', 'zzzzz-dz642-runningcontainr']] ],
13 controller: 'container_requests',
15 assert_response :success
16 found = assigns(:objects)
17 assert_includes(found.collect(&:container_uuid), nil,
18 "'container_uuid not in [zzzzz-dz642-queuedcontainer, zzzzz-dz642-runningcontainr]' filter should pass null")
21 test 'error message for non-array element in filters array' do
22 @controller = Arvados::V1::CollectionsController.new
23 authorize_with :active
25 filters: [{bogus: 'filter'}],
28 assert_match(/Invalid element in filters array/,
29 json_response['errors'].join(' '))
32 test 'error message for unsupported full text search' do
33 @controller = Arvados::V1::CollectionsController.new
34 authorize_with :active
36 filters: [['uuid', '@@', 'abcdef']],
39 assert_match(/no longer supported/, json_response['errors'].join(' '))
42 test 'api responses provide timestamps with nanoseconds' do
43 @controller = Arvados::V1::CollectionsController.new
44 authorize_with :active
46 assert_response :success
47 assert_not_empty json_response['items']
48 json_response['items'].each do |item|
49 %w(created_at modified_at).each do |attr|
50 # Pass fixtures with null timestamps.
51 next if item[attr].nil?
52 assert_match(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d.\d{9}Z$/, item[attr])
57 %w(< > <= >= =).each do |operator|
58 test "timestamp #{operator} filters work with nanosecond precision" do
59 # Python clients like Node Manager rely on this exact format.
60 # If you must change this format for some reason, make sure you
61 # coordinate the change with them.
62 expect_match = !!operator.index('=')
63 mine = act_as_user users(:active) do
64 Collection.create!(manifest_text: '')
66 timestamp = mine.modified_at.strftime('%Y-%m-%dT%H:%M:%S.%NZ')
67 @controller = Arvados::V1::CollectionsController.new
68 authorize_with :active
70 filters: [['modified_at', operator, timestamp],
71 ['uuid', '=', mine.uuid]],
73 assert_response :success
74 uuids = json_response['items'].map { |item| item['uuid'] }
76 assert_includes uuids, mine.uuid
78 assert_not_includes uuids, mine.uuid
83 [['prop1', '=', 'value1', [:collection_with_prop1_value1], [:collection_with_prop1_value2, :collection_with_prop2_1]],
84 ['prop1', '!=', 'value1', [:collection_with_prop1_value2, :collection_with_prop2_1], [:collection_with_prop1_value1]],
85 ['prop1', 'exists', true, [:collection_with_prop1_value1, :collection_with_prop1_value2, :collection_with_prop1_value3, :collection_with_prop1_other1], [:collection_with_prop2_1]],
86 ['prop1', 'exists', false, [:collection_with_prop2_1], [:collection_with_prop1_value1, :collection_with_prop1_value2, :collection_with_prop1_value3, :collection_with_prop1_other1]],
87 ['prop1', 'in', ['value1', 'value2'], [:collection_with_prop1_value1, :collection_with_prop1_value2], [:collection_with_prop1_value3, :collection_with_prop2_1]],
88 ['prop1', 'in', ['value1', 'valueX'], [:collection_with_prop1_value1], [:collection_with_prop1_value3, :collection_with_prop2_1]],
89 ['prop1', 'not in', ['value1', 'value2'], [:collection_with_prop1_value3, :collection_with_prop1_other1, :collection_with_prop2_1], [:collection_with_prop1_value1, :collection_with_prop1_value2]],
90 ['prop1', 'not in', ['value1', 'valueX'], [:collection_with_prop1_value2, :collection_with_prop1_value3, :collection_with_prop1_other1, :collection_with_prop2_1], [:collection_with_prop1_value1]],
91 ['prop1', '>', 'value2', [:collection_with_prop1_value3], [:collection_with_prop1_other1, :collection_with_prop1_value1]],
92 ['prop1', '<', 'value2', [:collection_with_prop1_other1, :collection_with_prop1_value1], [:collection_with_prop1_value2, :collection_with_prop1_value2]],
93 ['prop1', '<=', 'value2', [:collection_with_prop1_other1, :collection_with_prop1_value1, :collection_with_prop1_value2], [:collection_with_prop1_value3]],
94 ['prop1', '>=', 'value2', [:collection_with_prop1_value2, :collection_with_prop1_value3], [:collection_with_prop1_other1, :collection_with_prop1_value1]],
95 ['prop1', 'like', 'value%', [:collection_with_prop1_value1, :collection_with_prop1_value2, :collection_with_prop1_value3], [:collection_with_prop1_other1]],
96 ['prop1', 'like', '%1', [:collection_with_prop1_value1, :collection_with_prop1_other1], [:collection_with_prop1_value2, :collection_with_prop1_value3]],
97 ['prop1', 'ilike', 'VALUE%', [:collection_with_prop1_value1, :collection_with_prop1_value2, :collection_with_prop1_value3], [:collection_with_prop1_other1]],
98 ['prop2', '>', 1, [:collection_with_prop2_5], [:collection_with_prop2_1]],
99 ['prop2', '<', 5, [:collection_with_prop2_1], [:collection_with_prop2_5]],
100 ['prop2', '<=', 5, [:collection_with_prop2_1, :collection_with_prop2_5], []],
101 ['prop2', '>=', 1, [:collection_with_prop2_1, :collection_with_prop2_5], []],
102 ['<http://schema.org/example>', '=', "value1", [:collection_with_uri_prop], []],
103 ['listprop', 'contains', 'elem1', [:collection_with_list_prop_odd, :collection_with_listprop_elem1], [:collection_with_list_prop_even]],
104 ['listprop', '=', 'elem1', [:collection_with_listprop_elem1], [:collection_with_list_prop_odd]],
105 ['listprop', 'contains', 5, [:collection_with_list_prop_odd], [:collection_with_list_prop_even, :collection_with_listprop_elem1]],
106 ['listprop', 'contains', 'elem2', [:collection_with_list_prop_even], [:collection_with_list_prop_odd, :collection_with_listprop_elem1]],
107 ['listprop', 'contains', 'ELEM2', [], [:collection_with_list_prop_even]],
108 ['listprop', 'contains', 'elem8', [], [:collection_with_list_prop_even]],
109 ['listprop', 'contains', 4, [:collection_with_list_prop_even], [:collection_with_list_prop_odd, :collection_with_listprop_elem1]],
110 ].each do |prop, op, opr, inc, ex|
111 test "jsonb filter properties.#{prop} #{op} #{opr})" do
112 @controller = Arvados::V1::CollectionsController.new
113 authorize_with :admin
114 get :index, params: {
115 filters: SafeJSON.dump([ ["properties.#{prop}", op, opr] ]),
118 assert_response :success
119 found = assigns(:objects).collect(&:uuid)
122 assert_includes(found, collections(i).uuid)
126 assert_not_includes(found, collections(e).uuid)
131 test "jsonb hash 'exists' and '!=' filter" do
132 @controller = Arvados::V1::CollectionsController.new
133 authorize_with :admin
134 get :index, params: {
135 filters: [ ['properties.prop1', 'exists', true], ['properties.prop1', '!=', 'value1'] ]
137 assert_response :success
138 found = assigns(:objects).collect(&:uuid)
139 assert_equal found.length, 3
140 assert_not_includes(found, collections(:collection_with_prop1_value1).uuid)
141 assert_includes(found, collections(:collection_with_prop1_value2).uuid)
142 assert_includes(found, collections(:collection_with_prop1_value3).uuid)
143 assert_includes(found, collections(:collection_with_prop1_other1).uuid)
146 test "jsonb array 'exists'" do
147 @controller = Arvados::V1::CollectionsController.new
148 authorize_with :admin
149 get :index, params: {
150 filters: [ ['storage_classes_confirmed.default', 'exists', true] ]
152 assert_response :success
153 found = assigns(:objects).collect(&:uuid)
154 assert_equal 2, found.length
155 assert_not_includes(found,
156 collections(:storage_classes_desired_default_unconfirmed).uuid)
157 assert_includes(found,
158 collections(:storage_classes_desired_default_confirmed_default).uuid)
159 assert_includes(found,
160 collections(:storage_classes_desired_archive_confirmed_default).uuid)
163 test "jsonb hash alternate form 'exists' and '!=' filter" do
164 @controller = Arvados::V1::CollectionsController.new
165 authorize_with :admin
166 get :index, params: {
167 filters: [ ['properties', 'exists', 'prop1'], ['properties.prop1', '!=', 'value1'] ]
169 assert_response :success
170 found = assigns(:objects).collect(&:uuid)
171 assert_equal found.length, 3
172 assert_not_includes(found, collections(:collection_with_prop1_value1).uuid)
173 assert_includes(found, collections(:collection_with_prop1_value2).uuid)
174 assert_includes(found, collections(:collection_with_prop1_value3).uuid)
175 assert_includes(found, collections(:collection_with_prop1_other1).uuid)
178 test "jsonb array alternate form 'exists' filter" do
179 @controller = Arvados::V1::CollectionsController.new
180 authorize_with :admin
181 get :index, params: {
182 filters: [ ['storage_classes_confirmed', 'exists', 'default'] ]
184 assert_response :success
185 found = assigns(:objects).collect(&:uuid)
186 assert_equal 2, found.length
187 assert_not_includes(found,
188 collections(:storage_classes_desired_default_unconfirmed).uuid)
189 assert_includes(found,
190 collections(:storage_classes_desired_default_confirmed_default).uuid)
191 assert_includes(found,
192 collections(:storage_classes_desired_archive_confirmed_default).uuid)
195 test "jsonb 'exists' must be boolean" do
196 @controller = Arvados::V1::CollectionsController.new
197 authorize_with :admin
198 get :index, params: {
199 filters: [ ['properties.prop1', 'exists', nil] ]
202 assert_match(/Invalid operand '' for 'exists' must be true or false/,
203 json_response['errors'].join(' '))
206 test "jsonb checks column exists" do
207 @controller = Arvados::V1::CollectionsController.new
208 authorize_with :admin
209 get :index, params: {
210 filters: [ ['puppies.prop1', '=', 'value1'] ]
213 assert_match(/Invalid attribute 'puppies' for subproperty filter/,
214 json_response['errors'].join(' '))
217 test "jsonb checks column is valid" do
218 @controller = Arvados::V1::CollectionsController.new
219 authorize_with :admin
220 get :index, params: {
221 filters: [ ['name.prop1', '=', 'value1'] ]
224 assert_match(/Invalid attribute 'name' for subproperty filter/,
225 json_response['errors'].join(' '))
228 test "jsonb invalid operator" do
229 @controller = Arvados::V1::CollectionsController.new
230 authorize_with :admin
231 get :index, params: {
232 filters: [ ['properties.prop1', '###', 'value1'] ]
235 assert_match(/Invalid operator for subproperty search '###'/,
236 json_response['errors'].join(' '))
239 test "groups contents with properties filter succeeds on objects with properties field" do
240 @controller = Arvados::V1::GroupsController.new
241 authorize_with :admin
242 get :contents, params: {
244 ['properties', 'exists', 'foo'],
245 ['uuid', 'is_a', ["arvados#group","arvados#collection","arvados#containerRequest"]],
249 assert json_response['items'].length == 0
253 test "groups contents with properties filter succeeds on some objects with properties field" do
254 @controller = Arvados::V1::GroupsController.new
255 authorize_with :admin
256 get :contents, params: {
258 ['properties', 'exists', 'foo'],
259 ['uuid', 'is_a', ["arvados#group","arvados#workflow"]],
263 assert json_response['items'].length == 0
267 test "groups contents with properties filter fails on objects without properties field" do
268 @controller = Arvados::V1::GroupsController.new
269 authorize_with :admin
270 get :contents, params: {
272 ['properties', 'exists', 'foo'],
273 ['uuid', 'is_a', ["arvados#workflow"]],
277 assert_match(/Invalid attribute 'properties' for operator 'exists'.*on object type Workflow/, json_response['errors'].join(' '))
280 test "groups contents without filters and limit=0, count=none" do
281 @controller = Arvados::V1::GroupsController.new
282 authorize_with :admin
283 get :contents, params: {
288 assert json_response['items'].length == 0
291 test "replication_desired = 2" do
292 @controller = Arvados::V1::CollectionsController.new
293 authorize_with :admin
294 get :index, params: {
295 filters: SafeJSON.dump([ ['replication_desired', '=', 2] ])
297 assert_response :success
298 found = assigns(:objects).collect(&:uuid)
299 assert_includes(found, collections(:replication_desired_2_unconfirmed).uuid)
300 assert_includes(found, collections(:replication_desired_2_confirmed_2).uuid)
313 [nil, [{"foo" => "bar"}]],
314 [nil, {"foo" => "bar"}],
315 ].each do |results, operand|
316 test "storage_classes_desired contains #{operand.inspect}" do
317 @controller = Arvados::V1::CollectionsController.new
318 authorize_with(:active)
319 c = Collection.create!(
321 storage_classes_desired: ["foo", "bar", "baz"])
322 get :index, params: {
323 filters: [["storage_classes_desired", "contains", operand]],
329 assert_response :success
330 assert_equal results, json_response["items"].length
332 assert_equal c.uuid, json_response["items"][0]["uuid"]
337 test "collections properties contains top level key" do
338 @controller = Arvados::V1::CollectionsController.new
339 authorize_with(:active)
340 get :index, params: {
341 filters: [["properties", "contains", "prop1"]],
343 assert_response :success
344 assert_not_empty json_response["items"]
345 json_response["items"].each do |c|
346 assert c["properties"].has_key?("prop1")