2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * Modifications Copyright (C) 2020-2021,2023 Nordix Foundation.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.apex.plugins.context.schema.avro;
24 import static org.assertj.core.api.Assertions.assertThatThrownBy;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertThrows;
30 import java.io.IOException;
31 import java.util.HashMap;
32 import org.apache.avro.generic.GenericRecord;
33 import org.apache.avro.util.Utf8;
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.onap.policy.apex.context.ContextRuntimeException;
38 import org.onap.policy.apex.context.SchemaHelper;
39 import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
40 import org.onap.policy.apex.context.parameters.ContextParameterConstants;
41 import org.onap.policy.apex.context.parameters.SchemaParameters;
42 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
43 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
44 import org.onap.policy.apex.model.basicmodel.service.ModelService;
45 import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
46 import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchemas;
47 import org.onap.policy.common.parameters.ParameterService;
48 import org.onap.policy.common.utils.resources.TextFileUtils;
51 * The Class TestAvroSchemaMap.
53 * @author Liam Fallon (liam.fallon@ericsson.com)
56 public class AvroSchemaMapTest {
57 private final AxKey testKey = new AxArtifactKey("AvroTest", "0.0.1");
58 private AxContextSchemas schemas;
59 private String longMapSchema;
60 private String addressMapSchema;
61 private String addressMapSchemaInvalidFields;
66 * @throws IOException Signals that an I/O exception has occurred.
69 public void initTest() throws IOException {
70 schemas = new AxContextSchemas(new AxArtifactKey("AvroSchemas", "0.0.1"));
71 ModelService.registerModel(AxContextSchemas.class, schemas);
72 longMapSchema = TextFileUtils.getTextFileAsString("src/test/resources/avsc/MapExampleLong.avsc");
73 addressMapSchema = TextFileUtils.getTextFileAsString("src/test/resources/avsc/MapExampleAddress.avsc");
74 addressMapSchemaInvalidFields =
75 TextFileUtils.getTextFileAsString("src/test/resources/avsc/MapExampleAddressInvalidFields.avsc");
82 public void initContext() {
83 SchemaParameters schemaParameters = new SchemaParameters();
84 schemaParameters.setName(ContextParameterConstants.SCHEMA_GROUP_NAME);
85 schemaParameters.getSchemaHelperParameterMap().put("AVRO", new AvroSchemaHelperParameters());
86 ParameterService.register(schemaParameters);
93 public void clearContext() {
94 ParameterService.deregister(ContextParameterConstants.SCHEMA_GROUP_NAME);
98 * Test valid schemas with substitutions.
100 * @throws IOException Signals that an I/O exception has occurred.
103 public void testValidSubstitutions() throws IOException {
104 final String subst1 = "{\"type\":\"record\",\"name\":\"Subst1\","
105 + "\"fields\":[{\"name\": \"A_DasH_B\",\"type\":\"string\"}]}";
106 final AxContextSchema avroSubstSchema1 = new AxContextSchema(
107 new AxArtifactKey("AvroSubst1", "0.0.1"), "AVRO", subst1);
108 schemas.getSchemasMap().put(avroSubstSchema1.getKey(), avroSubstSchema1);
110 SchemaHelper schemaHelperSubst1 = new SchemaHelperFactory()
111 .createSchemaHelper(testKey, avroSubstSchema1.getKey());
112 final GenericRecord subst1A = (GenericRecord) schemaHelperSubst1.unmarshal("{\"A-B\":\"foo\"}");
113 assertEquals(new Utf8("foo"), subst1A.get("A_DasH_B"));
114 assertThatThrownBy(() -> subst1A.get("A-B")).hasMessage("Not a valid schema field: A-B");
116 final Throwable exception1 = assertThrows(ContextRuntimeException.class,
117 () -> schemaHelperSubst1.unmarshal("{\"A-B\":123}"));
118 assertNotNull(exception1.getCause());
119 assertEquals("Expected string. Got VALUE_NUMBER_INT", exception1.getCause().getMessage());
121 final String subst2 = "{\"type\":\"record\",\"name\":\"Subst2\","
122 + "\"fields\":[{\"name\": \"C_DoT_D\",\"type\":\"int\"}]}";
123 final AxContextSchema avroSubstSchema2 = new AxContextSchema(
124 new AxArtifactKey("AvroSubst2", "0.0.1"), "AVRO", subst2);
125 schemas.getSchemasMap().put(avroSubstSchema2.getKey(), avroSubstSchema2);
127 final SchemaHelper schemaHelperSubst2 = new SchemaHelperFactory()
128 .createSchemaHelper(testKey, avroSubstSchema2.getKey());
129 final GenericRecord subst2A = (GenericRecord) schemaHelperSubst2.unmarshal("{\"C.D\":123}");
130 assertEquals(123, subst2A.get("C_DoT_D"));
131 assertThatThrownBy(() -> subst2A.get("C.D")).hasMessage("Not a valid schema field: C.D");
133 final Throwable exception2 = assertThrows(ContextRuntimeException.class,
134 () -> schemaHelperSubst2.unmarshal("{\"C_DoT_D\":\"bar\"}"));
135 assertNotNull(exception2.getCause());
136 assertEquals("Expected int. Got VALUE_STRING", exception2.getCause().getMessage());
138 final String subst3 = "{\"type\":\"record\",\"name\":\"Subst3\","
139 + "\"fields\":[{\"name\": \"E_ColoN_F\",\"type\":\"boolean\"}]}";
140 final AxContextSchema avroSubstSchema3 = new AxContextSchema(
141 new AxArtifactKey("AvroSubst3", "0.0.1"), "AVRO", subst3);
142 schemas.getSchemasMap().put(avroSubstSchema3.getKey(), avroSubstSchema3);
144 final SchemaHelper schemaHelperSubst3 = new SchemaHelperFactory()
145 .createSchemaHelper(testKey, avroSubstSchema3.getKey());
146 final GenericRecord subst3A = (GenericRecord) schemaHelperSubst3.unmarshal("{\"E:F\":true}");
147 assertEquals(true, subst3A.get("E_ColoN_F"));
148 assertThatThrownBy(() -> subst3A.get("E:F")).hasMessage("Not a valid schema field: E:F");
150 final Throwable exception3 = assertThrows(ContextRuntimeException.class,
151 () -> schemaHelperSubst3.unmarshal("{\"E_ColoN_F\":\"gaz\"}"));
152 assertNotNull(exception3.getCause());
153 assertEquals("Expected boolean. Got VALUE_STRING", exception3.getCause().getMessage());
157 * Test invalid schemas without substitutions.
159 * @throws IOException Signals that an I/O exception has occurred.
162 public void testInValidSubstitutions() throws IOException {
163 final String fail1 = "{\"type\":\"record\",\"name\":\"Fail1\","
164 + "\"fields\":[{\"name\": \"A-B\",\"type\":\"string\"}]}";
165 final AxContextSchema avroFailSchema1 = new AxContextSchema(
166 new AxArtifactKey("AvroFail1", "0.0.1"), "AVRO", fail1);
167 schemas.getSchemasMap().put(avroFailSchema1.getKey(), avroFailSchema1);
169 SchemaHelperFactory sh = new SchemaHelperFactory();
170 AxArtifactKey ak = avroFailSchema1.getKey();
171 final Throwable exception1 = assertThrows(ContextRuntimeException.class,
172 () -> sh.createSchemaHelper(testKey, ak));
173 assertNotNull(exception1.getCause());
174 assertEquals("Illegal character in: A-B", exception1.getCause().getMessage());
176 final String fail2 = "{\"type\":\"record\",\"name\":\"Fail2\","
177 + "\"fields\":[{\"name\": \"C.D\",\"type\":\"int\"}]}";
178 final AxContextSchema avroFailSchema2 = new AxContextSchema(
179 new AxArtifactKey("AvroFail2", "0.0.1"), "AVRO", fail2);
180 schemas.getSchemasMap().put(avroFailSchema2.getKey(), avroFailSchema2);
182 AxArtifactKey ak2 = avroFailSchema2.getKey();
183 final Throwable exception2 = assertThrows(ContextRuntimeException.class,
184 () -> sh.createSchemaHelper(testKey, ak2));
185 assertNotNull(exception2.getCause());
186 assertEquals("Illegal character in: C.D", exception2.getCause().getMessage());
188 final String fail3 = "{\"type\":\"record\",\"name\":\"Fail3\","
189 + "\"fields\":[{\"name\": \"E:F\",\"type\":\"boolean\"}]}";
190 final AxContextSchema avroFailSchema3 = new AxContextSchema(
191 new AxArtifactKey("AvroFail3", "0.0.1"), "AVRO", fail3);
192 schemas.getSchemasMap().put(avroFailSchema3.getKey(), avroFailSchema3);
193 AxArtifactKey ak3 = avroFailSchema3.getKey();
194 final Throwable exception3 = assertThrows(ContextRuntimeException.class,
195 () -> sh.createSchemaHelper(testKey, ak3));
196 assertNotNull(exception3.getCause());
197 assertEquals("Illegal character in: E:F", exception3.getCause().getMessage());
203 * @throws IOException Signals that an I/O exception has occurred.
206 public void testMapInit() throws IOException {
207 final AxContextSchema avroSchema =
208 new AxContextSchema(new AxArtifactKey("AvroRecord", "0.0.1"), "AVRO", addressMapSchema);
210 schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema);
211 final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey());
213 final HashMap<?, ?> newMapEmpty = (HashMap<?, ?>) schemaHelper.createNewInstance();
214 assertEquals(0, newMapEmpty.size());
216 final String inString = TextFileUtils.getTextFileAsString("src/test/resources/data/MapExampleAddressFull.json");
217 final HashMap<?, ?> newMapFull = (HashMap<?, ?>) schemaHelper.createNewInstance(inString);
219 assertEquals("{\"streetaddress\": \"221 B Baker St.\", \"city\": \"London\"}",
220 newMapFull.get(new Utf8("address2")).toString());
224 * Test long map unmarshal marshal.
226 * @throws IOException Signals that an I/O exception has occurred.
229 public void testLongMapUnmarshalMarshal() throws IOException {
230 final AxContextSchema avroSchema =
231 new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", longMapSchema);
233 schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema);
234 final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey());
236 testUnmarshalMarshal(schemaHelper, "src/test/resources/data/MapExampleLongNull.json");
237 testUnmarshalMarshal(schemaHelper, "src/test/resources/data/MapExampleLongFull.json");
241 * Test address map unmarshal marshal.
243 * @throws IOException Signals that an I/O exception has occurred.
246 public void testAddressMapUnmarshalMarshal() throws IOException {
247 final AxContextSchema avroSchema =
248 new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", addressMapSchema);
250 schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema);
251 final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey());
253 testUnmarshalMarshal(schemaHelper, "src/test/resources/data/MapExampleAddressNull.json");
254 testUnmarshalMarshal(schemaHelper, "src/test/resources/data/MapExampleAddressFull.json");
258 * Test sub record create.
260 * @throws IOException Signals that an I/O exception has occurred.
263 public void testSubRecordCreateRecord() throws IOException {
264 final AxContextSchema avroSchema =
265 new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", addressMapSchema);
267 schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema);
268 final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey());
270 final GenericRecord subRecord = (GenericRecord) schemaHelper.createNewSubInstance("AddressUSRecord");
271 assertThatThrownBy(() -> subRecord.get("streetAddress")).hasMessage("Not a valid schema field: streetAddress");
276 * Test address map unmarshal marshal invalid fields.
278 * @throws IOException Signals that an I/O exception has occurred.
281 public void testAddressMapUnmarshalMarshalInvalidFields() throws IOException {
282 final AxContextSchema avroSchema =
283 new AxContextSchema(new AxArtifactKey("AvroMap", "0.0.1"), "AVRO", addressMapSchemaInvalidFields);
285 schemas.getSchemasMap().put(avroSchema.getKey(), avroSchema);
286 final SchemaHelper schemaHelper = new SchemaHelperFactory().createSchemaHelper(testKey, avroSchema.getKey());
288 testUnmarshalMarshal(schemaHelper, "src/test/resources/data/MapExampleAddressInvalidFields.json");
290 String vals = TextFileUtils.getTextFileAsString("src/test/resources/data/MapExampleAddressInvalidFields.json");
291 final HashMap<?, ?> newMapFull = (HashMap<?, ?>) schemaHelper.createNewInstance(vals);
292 final String expect = "{\"street_DasH_address\": \"Wayne Manor\", \"the_DoT_city\": \"Gotham City\", "
293 + "\"the_ColoN_code\": \"BatCave7\"}";
294 assertEquals(expect, newMapFull.get(new Utf8("address_DoT_3")).toString());
298 * Test unmarshal marshal.
300 * @param schemaHelper the schema helper
301 * @param fileName the file name
302 * @throws IOException Signals that an I/O exception has occurred.
304 private void testUnmarshalMarshal(final SchemaHelper schemaHelper, final String fileName) throws IOException {
305 final String originalInString = TextFileUtils.getTextFileAsString(fileName);
306 final HashMap<?, ?> firstDecodedMap = (HashMap<?, ?>) schemaHelper.unmarshal(originalInString);
308 final String outString = schemaHelper.marshal2String(firstDecodedMap);
310 final File tempOutFile = File.createTempFile("ApexAvro", ".json");
311 TextFileUtils.putStringAsFile(outString, tempOutFile);
313 final String decodeEncodeInString = TextFileUtils.getTextFileAsString(fileName);
314 tempOutFile.delete();
316 final HashMap<?, ?> secondDecodedMap = (HashMap<?, ?>) schemaHelper.unmarshal(decodeEncodeInString);
318 // Now check that our doubly encoded map equals the first decoded map, Java map equals
319 // checks values and keys
320 assertEquals(firstDecodedMap, secondDecodedMap);