1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.adservices.service.js;
18 
19 
20 import static com.android.adservices.service.js.JSScriptArgument.arrayArg;
21 import static com.android.adservices.service.js.JSScriptArgument.jsonArg;
22 import static com.android.adservices.service.js.JSScriptArgument.jsonArrayArg;
23 import static com.android.adservices.service.js.JSScriptArgument.jsonArrayArgNoValidation;
24 import static com.android.adservices.service.js.JSScriptArgument.numericArg;
25 import static com.android.adservices.service.js.JSScriptArgument.recordArg;
26 import static com.android.adservices.service.js.JSScriptArgument.stringArg;
27 import static com.android.adservices.service.js.JSScriptArgument.stringMapToRecordArg;
28 
29 import static com.google.common.truth.Truth.assertThat;
30 
31 import static org.junit.Assert.assertThrows;
32 
33 import com.google.common.collect.ImmutableMap;
34 import com.google.common.collect.ImmutableSet;
35 
36 import org.json.JSONException;
37 import org.junit.Test;
38 
39 import java.util.Map;
40 
41 public class JSScriptArgumentTest {
42     private static final String VALID_TEST_OBJECT_JSON_ARRAY =
43             "["
44                     + "{"
45                     + "\"name\":\"John\","
46                     + "\"age\":30,"
47                     + "\"city\":\"New York\""
48                     + "},"
49                     + "{"
50                     + "\"name\":\"Alice\","
51                     + "\"age\":25,"
52                     + "\"city\":\"Los Angeles\""
53                     + "},"
54                     + "{"
55                     + "\"name\":\"Bob\","
56                     + "\"age\":35,"
57                     + "\"city\":\"Chicago\""
58                     + "}]";
59 
60     private static final ImmutableSet<TestObject> VALID_TEST_OBJECT_ARRAY =
61             ImmutableSet.of(
62                     new TestObject("John", 30, "New York"),
63                     new TestObject("Alice", 25, "Los Angeles"),
64                     new TestObject("Bob", 35, "Chicago"));
65 
66     @Test
test_stringArg_validString_returnsSuccess()67     public void test_stringArg_validString_returnsSuccess() {
68         JSScriptArgument arg = stringArg("stringArg", "value");
69         assertThat(arg.variableDeclaration()).isEqualTo("const stringArg = \"value\";");
70     }
71 
72     @Test
test_numericArg_validInteger_returnsSuccess()73     public void test_numericArg_validInteger_returnsSuccess() {
74         JSScriptArgument arg = numericArg("numericArg", 1);
75         assertThat(arg.variableDeclaration()).isEqualTo("const numericArg = 1;");
76     }
77 
78     @Test
test_numericArg_validFloat_returnsSuccess()79     public void test_numericArg_validFloat_returnsSuccess() {
80         JSScriptArgument arg = numericArg("numericArg", 1.001);
81         assertThat(arg.variableDeclaration()).isEqualTo("const numericArg = 1.001;");
82     }
83 
84     @Test
test_jsonArg_validJson_returnsSuccess()85     public void test_jsonArg_validJson_returnsSuccess() throws JSONException {
86         final String jsonValue = "{\"intField\": 123, \"stringField\": \"value\"}";
87         JSScriptArgument arg = jsonArg("jsonArg", jsonValue);
88         assertThat(arg.variableDeclaration())
89                 .isEqualTo(String.format("const jsonArg = %s;", jsonValue));
90     }
91 
92     @Test
test_jsonArrayArg_validJsonArray_returnsSuccess()93     public void test_jsonArrayArg_validJsonArray_returnsSuccess() throws JSONException {
94         JSScriptArgument arg = jsonArrayArg("jsonArrayArg", VALID_TEST_OBJECT_JSON_ARRAY);
95         assertThat(arg.variableDeclaration())
96                 .isEqualTo(String.format("const jsonArrayArg = %s;", VALID_TEST_OBJECT_JSON_ARRAY));
97     }
98 
99     @Test
test_jsonArrayArg_invalidJsonArray_throwsException()100     public void test_jsonArrayArg_invalidJsonArray_throwsException() {
101         final String jsonArrayValue = "this is an invalid json array";
102         assertThrows(JSONException.class, () -> jsonArrayArg("jsonArrayArg", jsonArrayValue));
103     }
104 
105     @Test
test_jsonArrayArgNoValidation_emptyCollection_returnsEmptyJson()106     public void test_jsonArrayArgNoValidation_emptyCollection_returnsEmptyJson() {
107         JSScriptArgument arg =
108                 jsonArrayArgNoValidation(
109                         "jsonArrayArg", ImmutableSet.of(), TestObject::serializeEntryToJson);
110         assertThat(arg.variableDeclaration()).isEqualTo("const jsonArrayArg = [];");
111     }
112 
113     @Test
test_jsonArrayArgNoValidation_testObjectMarshaling_returnsMarshalledJson()114     public void test_jsonArrayArgNoValidation_testObjectMarshaling_returnsMarshalledJson() {
115         JSScriptArgument arg =
116                 jsonArrayArgNoValidation(
117                         "jsonArrayArg", VALID_TEST_OBJECT_ARRAY, TestObject::serializeEntryToJson);
118         assertThat(arg.variableDeclaration())
119                 .isEqualTo(String.format("const jsonArrayArg = %s;", VALID_TEST_OBJECT_JSON_ARRAY));
120     }
121 
122     @Test
test_jsonArg_invalidJson_throwsException()123     public void test_jsonArg_invalidJson_throwsException() throws JSONException {
124         // Missing closing }
125         final String jsonValue = "{\"intField\": 123, \"stringField\": \"value\"";
126         assertThrows(JSONException.class, () -> jsonArg("jsonArg", jsonValue));
127     }
128 
129     @Test
test_arrayArg_validArray_returnsSuccess()130     public void test_arrayArg_validArray_returnsSuccess() throws JSONException {
131         JSScriptArgument arg =
132                 JSScriptArgument.arrayArg(
133                         "arrayArg", stringArg("ignored", "value1"), stringArg("ignored", "value2"));
134 
135         assertThat(arg.variableDeclaration())
136                 .isEqualTo("const arrayArg = [\n\"value1\"," + "\n\"value2\"\n];");
137     }
138 
139     @Test
test_recordArg_validInput_returnsSuccess()140     public void test_recordArg_validInput_returnsSuccess() throws JSONException {
141         JSScriptArgument arg =
142                 recordArg(
143                         "recordArg",
144                         numericArg("intField", 123),
145                         arrayArg(
146                                 "arrayField",
147                                 stringArg("ignored", "value1"),
148                                 stringArg("ignored", "value2")));
149         assertThat(arg.variableDeclaration())
150                 .isEqualTo(
151                         "const recordArg = {\n\"intField\": 123,\n\"arrayField\": [\n\"value1\","
152                                 + "\n\"value2\"\n]\n};");
153     }
154 
155     @Test
test_stringMapToRecordArg_validStringMap_returnsSuccess()156     public void test_stringMapToRecordArg_validStringMap_returnsSuccess() throws JSONException {
157         Map<String, String> signals =
158                 ImmutableMap.of(
159                         "key1",
160                         "{\"signals\":1}",
161                         "key2",
162                         "{\"signals\":2}",
163                         "key3",
164                         "{\"signals\":3}");
165         JSScriptArgument arg = stringMapToRecordArg("stringMapToRecordArg", signals);
166         assertThat(arg.variableDeclaration())
167                 .isEqualTo(
168                         "const stringMapToRecordArg = {\n"
169                                 + "\"key1\": {\"signals\":1},\n"
170                                 + "\"key2\": {\"signals\":2},\n"
171                                 + "\"key3\": {\"signals\":3}\n"
172                                 + "};");
173     }
174 
175     private static class TestObject {
176         public String name;
177         public int age;
178         public String city;
179 
TestObject(String name, int age, String city)180         TestObject(String name, int age, String city) {
181             this.name = name;
182             this.age = age;
183             this.city = city;
184         }
185 
serializeEntryToJson(TestObject value, StringBuilder accumulator)186         static void serializeEntryToJson(TestObject value, StringBuilder accumulator) {
187             accumulator.append("{");
188             accumulator.append("\"name\":\"").append(value.name).append("\",");
189             accumulator.append("\"age\":").append(value.age).append(",");
190             accumulator.append("\"city\":\"").append(value.city).append("\"");
191             accumulator.append("}");
192         }
193     }
194 }
195