aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/test/java/com/yahoo/search/query/profile/types/test/MandatoryTestCase.java
blob: 31136a1a558e63259df07f641093b2f85627422c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.query.profile.types.test;

import com.yahoo.component.ComponentId;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.search.Query;
import com.yahoo.search.query.profile.QueryProfile;
import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry;
import com.yahoo.search.query.profile.types.FieldDescription;
import com.yahoo.search.query.profile.types.FieldType;
import com.yahoo.search.query.profile.types.QueryProfileType;
import com.yahoo.search.query.profile.types.QueryProfileTypeRegistry;
import com.yahoo.search.test.QueryTestCase;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 * @author bratseth
 */
public class MandatoryTestCase {

    private static class Fixture1 {

        final QueryProfileRegistry registry = new QueryProfileRegistry();
        final QueryProfileTypeRegistry typeRegistry = new QueryProfileTypeRegistry();
        final QueryProfileType type = new QueryProfileType(new ComponentId("testtype"));
        final QueryProfileType user = new QueryProfileType(new ComponentId("user"));

        public Fixture1() {
            typeRegistry.register(type);
            typeRegistry.register(user);

            addTypeFields(type, typeRegistry);
            addUserFields(user, typeRegistry);
        }

        private static void addTypeFields(QueryProfileType type, QueryProfileTypeRegistry registry) {
            type.addField(new FieldDescription("myString", FieldType.fromString("string", registry), true));
            type.addField(new FieldDescription("myInteger", FieldType.fromString("integer", registry)));
            type.addField(new FieldDescription("myLong", FieldType.fromString("long", registry)));
            type.addField(new FieldDescription("myFloat", FieldType.fromString("float", registry)));
            type.addField(new FieldDescription("myDouble", FieldType.fromString("double", registry)));
            type.addField(new FieldDescription("myQueryProfile", FieldType.fromString("query-profile", registry)));
            type.addField(new FieldDescription("myUserQueryProfile", FieldType.fromString("query-profile:user", registry), true));
        }

        private static void addUserFields(QueryProfileType user, QueryProfileTypeRegistry registry) {
            user.addField(new FieldDescription("myUserString", FieldType.fromString("string", registry), true));
            user.addField(new FieldDescription("myUserInteger", FieldType.fromString("integer", registry), true));
        }

    }

    @Test
    void testMandatoryFullySpecifiedQueryProfile() {
        Fixture1 fixture = new Fixture1();

        QueryProfile test = new QueryProfile("test");
        test.setType(fixture.type);
        test.set("myString", "aString", fixture.registry);
        fixture.registry.register(test);

        QueryProfile myUser = new QueryProfile("user");
        myUser.setType(fixture.user);
        myUser.set("myUserInteger", 1, fixture.registry);
        myUser.set("myUserString", 1, fixture.registry);
        test.set("myUserQueryProfile", myUser, fixture.registry);
        fixture.registry.register(myUser);

        CompiledQueryProfileRegistry cRegistry = fixture.registry.compile();

        // Fully specified request
        assertError(null, new Query(QueryTestCase.httpEncode("?queryProfile=test"), cRegistry.getComponent("test")));
    }

    @Test
    void testMandatoryRequestPropertiesNeeded() {
        Fixture1 fixture = new Fixture1();

        QueryProfile test = new QueryProfile("test");
        test.setType(fixture.type);
        fixture.registry.register(test);

        QueryProfile myUser = new QueryProfile("user");
        myUser.setType(fixture.user);
        myUser.set("myUserInteger", 1, fixture.registry);
        test.set("myUserQueryProfile", myUser, fixture.registry);
        fixture.registry.register(myUser);

        CompiledQueryProfileRegistry cRegistry = fixture.registry.compile();

        // Underspecified request 1
        assertError("Incomplete query: Parameter 'myString' is mandatory in query profile 'test' of type 'testtype' but is not set",
                new Query(HttpRequest.createTestRequest("", Method.GET), cRegistry.getComponent("test")));

        // Underspecified request 2
        assertError("Incomplete query: Parameter 'myUserQueryProfile.myUserString' is mandatory in query profile 'test' of type 'testtype' but is not set",
                new Query(HttpRequest.createTestRequest("?myString=aString", Method.GET), cRegistry.getComponent("test")));

        // Fully specified request
        assertError(null, new Query(HttpRequest.createTestRequest("?myString=aString&myUserQueryProfile.myUserString=userString", Method.GET), cRegistry.getComponent("test")));
    }

    /** Same as above except the whole thing is nested in maps */
    @Test
    void testMandatoryNestedInMaps() {
        Fixture1 fixture = new Fixture1();

        QueryProfile topMap = new QueryProfile("topMap");
        fixture.registry.register(topMap);

        QueryProfile subMap = new QueryProfile("topSubMap");
        topMap.set("subMap", subMap, fixture.registry);
        fixture.registry.register(subMap);

        QueryProfile test = new QueryProfile("test");
        test.setType(fixture.type);
        subMap.set("test", test, fixture.registry);
        fixture.registry.register(test);

        QueryProfile myUser = new QueryProfile("user");
        myUser.setType(fixture.user);
        myUser.set("myUserInteger", 1, fixture.registry);
        test.set("myUserQueryProfile", myUser, fixture.registry);
        fixture.registry.register(myUser);


        CompiledQueryProfileRegistry cRegistry = fixture.registry.compile();

        // Underspecified request 1
        assertError("Incomplete query: Parameter 'subMap.test.myString' is mandatory in query profile 'topMap' but is not set",
                new Query(HttpRequest.createTestRequest("", Method.GET), cRegistry.getComponent("topMap")));

        // Underspecified request 2
        assertError("Incomplete query: Parameter 'subMap.test.myUserQueryProfile.myUserString' is mandatory in query profile 'topMap' but is not set",
                new Query(HttpRequest.createTestRequest("?subMap.test.myString=aString", Method.GET), cRegistry.getComponent("topMap")));

        // Fully specified request
        assertError(null, new Query(HttpRequest.createTestRequest("?subMap.test.myString=aString&subMap.test.myUserQueryProfile.myUserString=userString", Method.GET), cRegistry.getComponent("topMap")));
    }

    /** Here, no user query profile is referenced in the query profile, but one is chosen in the request */
    @Test
    void testMandatoryUserProfileSetInRequest() {
        Fixture1 fixture = new Fixture1();

        QueryProfile test = new QueryProfile("test");
        test.setType(fixture.type);

        QueryProfile myUser = new QueryProfile("user");
        myUser.setType(fixture.user);
        myUser.set("myUserInteger", 1, null);

        QueryProfileRegistry registry = new QueryProfileRegistry();
        registry.register(test);
        registry.register(myUser);
        CompiledQueryProfileRegistry cRegistry = registry.compile();

        // Underspecified request 1
        assertError("Incomplete query: Parameter 'myUserQueryProfile' is mandatory in query profile 'test' of type 'testtype' but is not set",
                new Query(HttpRequest.createTestRequest("?myString=aString", Method.GET), cRegistry.getComponent("test")));

        // Underspecified request 1
        assertError("Incomplete query: Parameter 'myUserQueryProfile.myUserString' is mandatory in query profile 'test' of type 'testtype' but is not set",
                new Query(HttpRequest.createTestRequest("?myString=aString&myUserQueryProfile=user", Method.GET), cRegistry.getComponent("test")));

        // Fully specified request
        assertError(null, new Query(HttpRequest.createTestRequest("?myString=aString&myUserQueryProfile=user&myUserQueryProfile.myUserString=userString", Method.GET), cRegistry.getComponent("test")));
    }

    /** Here, a partially specified query profile is added to a non-mandatory field, making the request underspecified */
    @Test
    void testNonMandatoryUnderspecifiedUserProfileSetInRequest() {
        Fixture1 fixture = new Fixture1();

        QueryProfile test = new QueryProfile("test");
        test.setType(fixture.type);
        fixture.registry.register(test);

        QueryProfile myUser = new QueryProfile("user");
        myUser.setType(fixture.user);
        myUser.set("myUserInteger", 1, fixture.registry);
        myUser.set("myUserString", "userValue", fixture.registry);
        test.set("myUserQueryProfile", myUser, fixture.registry);
        fixture.registry.register(myUser);

        QueryProfile otherUser = new QueryProfile("otherUser");
        otherUser.setType(fixture.user);
        otherUser.set("myUserInteger", 2, fixture.registry);
        fixture.registry.register(otherUser);

        CompiledQueryProfileRegistry cRegistry = fixture.registry.compile();

        // Fully specified request
        assertError(null, new Query(HttpRequest.createTestRequest("?myString=aString", Method.GET), cRegistry.getComponent("test")));

        // Underspecified because an underspecified profile is added
        assertError("Incomplete query: Parameter 'myQueryProfile.myUserString' is mandatory in query profile 'test' of type 'testtype' but is not set",
                new Query(HttpRequest.createTestRequest("?myString=aString&myQueryProfile=otherUser", Method.GET), cRegistry.getComponent("test")));

        // Back to fully specified
        assertError(null, new Query(HttpRequest.createTestRequest("?myString=aString&myQueryProfile=otherUser&myQueryProfile.myUserString=userString", Method.GET), cRegistry.getComponent("test")));
    }

    private static class Fixture2 {

        final QueryProfileRegistry registry = new QueryProfileRegistry();
        final QueryProfileTypeRegistry typeRegistry = new QueryProfileTypeRegistry();
        final QueryProfileType rootType = new QueryProfileType(new ComponentId("root"));
        final QueryProfileType mandatoryType = new QueryProfileType(new ComponentId("mandatory-type"));

        public Fixture2() {
            typeRegistry.register(rootType);
            typeRegistry.register(mandatoryType);

            mandatoryType.inherited().add(rootType);
            mandatoryType.addField(new FieldDescription("foobar", FieldType.fromString("string", typeRegistry), true));
        }

    }

    @Test
    void testMandatoryInParentType() {
        Fixture2 fixture = new Fixture2();

        QueryProfile defaultProfile = new QueryProfile("default");
        defaultProfile.setType(fixture.rootType);

        QueryProfile mandatoryProfile = new QueryProfile("mandatory");
        mandatoryProfile.setType(fixture.mandatoryType);

        fixture.registry.register(defaultProfile);
        fixture.registry.register(mandatoryProfile);
        CompiledQueryProfileRegistry cRegistry = fixture.registry.compile();

        assertError("Incomplete query: Parameter 'foobar' is mandatory in query profile 'mandatory' of type 'mandatory-type' but is not set",
                new Query(QueryTestCase.httpEncode("?queryProfile=mandatory"), cRegistry.getComponent("mandatory")));
    }

    @Test
    void testMandatoryInParentTypeWithInheritance() {
        Fixture2 fixture = new Fixture2();

        QueryProfile defaultProfile = new QueryProfile("default");
        defaultProfile.setType(fixture.rootType);

        QueryProfile mandatoryProfile = new QueryProfile("mandatory");
        mandatoryProfile.addInherited(defaultProfile); // The single difference from the test above
        mandatoryProfile.setType(fixture.mandatoryType);

        fixture.registry.register(defaultProfile);
        fixture.registry.register(mandatoryProfile);
        CompiledQueryProfileRegistry cRegistry = fixture.registry.compile();

        assertError("Incomplete query: Parameter 'foobar' is mandatory in query profile 'mandatory' of type 'mandatory-type' but is not set",
                new Query(QueryTestCase.httpEncode("?queryProfile=mandatory"), cRegistry.getComponent("mandatory")));
    }

    private void assertError(String message,Query query) {
        assertEquals(message, query.validate());
    }

}