From bdfac35395b153ed239b84f6095f559ba2d1fa50 Mon Sep 17 00:00:00 2001 From: "Singal, Kapil (ks220y)" Date: Tue, 4 Sep 2018 13:24:11 -0400 Subject: [PATCH] SDN Controller Blueprints Rest Adaptor Creating SDN Controller Blueprints Rest Adaptor Junit Tests Change-Id: Id388b0905d570a75b6842cc69b52f2d26e3b060e Issue-ID: CCSDK-512 Signed-off-by: Singal, Kapil (ks220y) --- .../AbstractConfigRestClientAdapterTest.java | 53 ++++++++++ .../service/GenericRestClientServiceTest.java | 116 +++++++++++++++++++++ .../rest/adaptor/service/SSLClientServiceTest.java | 116 +++++++++++++++++++++ .../adaptor/utils/RestTemplateFactoryTest.java | 32 ++++++ .../test/resources/config-rest-adaptor.properties | 29 ++++++ .../src/test/resources/keystore.client.p12 | Bin 0 -> 1456 bytes .../src/test/resources/truststore.client.jks | Bin 0 -> 1228 bytes 7 files changed, 346 insertions(+) create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/AbstractConfigRestClientAdapterTest.java create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/GenericRestClientServiceTest.java create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/SSLClientServiceTest.java create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/utils/RestTemplateFactoryTest.java create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/config-rest-adaptor.properties create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/keystore.client.p12 create mode 100644 blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/truststore.client.jks diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/AbstractConfigRestClientAdapterTest.java b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/AbstractConfigRestClientAdapterTest.java new file mode 100644 index 000000000..e698567a0 --- /dev/null +++ b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/AbstractConfigRestClientAdapterTest.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2017-2018 AT&T Intellectual Property. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.onap.ccsdk.config.rest.adaptor.service; + +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class AbstractConfigRestClientAdapterTest { + + Map properties = new HashMap<>(); + + @Before + public void setup() throws Exception { + String propertyfile = "src/test/resources/config-rest-adaptor.properties"; + + Properties restProperties = new Properties(); + restProperties.load(new FileInputStream(propertyfile)); + + properties.putAll(restProperties.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString()))); + } + + @Test + public void testInitGenericRestClient() throws Exception { + ConfigRestClientServiceAdapter genericRestClient = new GenericRestClientAdapterImpl(properties, "modelservice"); + Assert.assertNotNull(genericRestClient); + } + + @Test + public void testInitSSLClient() throws Exception { + ConfigRestClientServiceAdapter sslClient = new SSLRestClientAdapterImpl(properties, "aai"); + Assert.assertNotNull(sslClient); + } + +} diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/GenericRestClientServiceTest.java b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/GenericRestClientServiceTest.java new file mode 100644 index 000000000..65a90429d --- /dev/null +++ b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/GenericRestClientServiceTest.java @@ -0,0 +1,116 @@ +/* + * Copyright © 2017-2018 AT&T Intellectual Property. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.onap.ccsdk.config.rest.adaptor.service; + +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.onap.ccsdk.config.rest.adaptor.ConfigRestAdaptorException; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +@SuppressWarnings("unchecked") +@RunWith(PowerMockRunner.class) +@PowerMockIgnore("javax.net.ssl.*") +@PrepareForTest({AbstractConfigRestClientAdapter.class}) +public class GenericRestClientServiceTest { + + ConfigRestAdaptorService configRestAdaptorService; + + RestTemplate mockRestTemplate = mock(RestTemplate.class); + + String path = "path"; + + @Before + public void before() throws Exception { + whenNew(RestTemplate.class).withAnyArguments().thenReturn(mockRestTemplate); + + String propertyDir = "src/test/resources"; + configRestAdaptorService = new ConfigRestAdaptorServiceImpl(propertyDir); + } + + @Test + public void testGetResource() throws Exception { + String responseBody = "sampleBodyString"; + ResponseEntity response = new ResponseEntity(responseBody, HttpStatus.OK); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.GET), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + String body = configRestAdaptorService.getResource("modelservice", path, String.class); + + Assert.assertEquals(responseBody, body); + } + + @Test + public void testPostResource() throws Exception { + String responseBody = "sampleBodyString"; + ResponseEntity response = new ResponseEntity(responseBody, HttpStatus.OK); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.POST), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + String body = configRestAdaptorService.postResource("modelservice", path, null, String.class); + + Assert.assertEquals(responseBody, body); + } + + @Test + public void testExchange() throws Exception { + String responseBody = "sampleBodyString"; + ResponseEntity response = new ResponseEntity(responseBody, HttpStatus.OK); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.GET), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + String body = configRestAdaptorService.exchangeResource("modelservice", path, null, String.class, "GET"); + + Assert.assertEquals(responseBody, body); + } + + @Test(expected = ConfigRestAdaptorException.class) + public void testGetResourceError() throws Exception { + ResponseEntity response = new ResponseEntity("", HttpStatus.INTERNAL_SERVER_ERROR); + when(mockRestTemplate.getForEntity(Matchers.endsWith(path), Matchers.any())).thenReturn(response); + + configRestAdaptorService.getResource("modelservice", path, String.class); + } + + @Test(expected = ConfigRestAdaptorException.class) + public void testPostResourceError() throws Exception { + ResponseEntity response = new ResponseEntity("", HttpStatus.INTERNAL_SERVER_ERROR); + when(mockRestTemplate.postForEntity(Matchers.endsWith(path), Matchers.anyObject(), Matchers.any())) + .thenReturn(response); + + configRestAdaptorService.postResource("modelservice", path, null, String.class); + } + + @Test(expected = ConfigRestAdaptorException.class) + public void testExchangeError() throws Exception { + ResponseEntity response = new ResponseEntity("", HttpStatus.INTERNAL_SERVER_ERROR); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.GET), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + configRestAdaptorService.exchangeResource("modelservice", path, null, String.class, "GET"); + } +} diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/SSLClientServiceTest.java b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/SSLClientServiceTest.java new file mode 100644 index 000000000..4592de819 --- /dev/null +++ b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/service/SSLClientServiceTest.java @@ -0,0 +1,116 @@ +/* + * Copyright © 2017-2018 AT&T Intellectual Property. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.onap.ccsdk.config.rest.adaptor.service; + +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.onap.ccsdk.config.rest.adaptor.ConfigRestAdaptorException; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +@SuppressWarnings("unchecked") +@RunWith(PowerMockRunner.class) +@PowerMockIgnore("javax.net.ssl.*") +@PrepareForTest({AbstractConfigRestClientAdapter.class}) +public class SSLClientServiceTest { + + ConfigRestAdaptorService configRestAdaptorService; + + RestTemplate mockRestTemplate = mock(RestTemplate.class); + + String path = "path"; + + @Before + public void before() throws Exception { + whenNew(RestTemplate.class).withAnyArguments().thenReturn(mockRestTemplate); + + String propertyDir = "src/test/resources"; + configRestAdaptorService = new ConfigRestAdaptorServiceImpl(propertyDir); + } + + @Test + public void testGetResource() throws Exception { + String responseBody = "sampleBodyString"; + ResponseEntity response = new ResponseEntity(responseBody, HttpStatus.OK); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.GET), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + String body = configRestAdaptorService.getResource("aai", path, String.class); + + Assert.assertEquals(responseBody, body); + } + + @Test + public void testPostResource() throws Exception { + String responseBody = "sampleBodyString"; + ResponseEntity response = new ResponseEntity(responseBody, HttpStatus.OK); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.POST), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + String body = configRestAdaptorService.postResource("aai", path, null, String.class); + + Assert.assertEquals(responseBody, body); + } + + @Test + public void testExchange() throws Exception { + String responseBody = "sampleBodyString"; + ResponseEntity response = new ResponseEntity(responseBody, HttpStatus.OK); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.GET), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + String body = configRestAdaptorService.exchangeResource("aai", path, null, String.class, "GET"); + + Assert.assertEquals(responseBody, body); + } + + @Test(expected = ConfigRestAdaptorException.class) + public void testGetResourceError() throws Exception { + ResponseEntity response = new ResponseEntity("", HttpStatus.INTERNAL_SERVER_ERROR); + when(mockRestTemplate.getForEntity(Matchers.endsWith(path), Matchers.any())).thenReturn(response); + + configRestAdaptorService.getResource("aai", path, String.class); + } + + @Test(expected = ConfigRestAdaptorException.class) + public void testPostResourceError() throws Exception { + ResponseEntity response = new ResponseEntity("", HttpStatus.INTERNAL_SERVER_ERROR); + when(mockRestTemplate.postForEntity(Matchers.endsWith(path), Matchers.anyObject(), Matchers.any())) + .thenReturn(response); + + configRestAdaptorService.postResource("aai", path, null, String.class); + } + + @Test(expected = ConfigRestAdaptorException.class) + public void testExchangeError() throws Exception { + ResponseEntity response = new ResponseEntity("", HttpStatus.INTERNAL_SERVER_ERROR); + when(mockRestTemplate.exchange(Matchers.endsWith(path), Matchers.eq(HttpMethod.GET), Matchers.any(), + Matchers.any(Class.class))).thenReturn(response); + + configRestAdaptorService.exchangeResource("aai", path, null, String.class, "GET"); + } +} diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/utils/RestTemplateFactoryTest.java b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/utils/RestTemplateFactoryTest.java new file mode 100644 index 000000000..a01630d62 --- /dev/null +++ b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/java/org/onap/ccsdk/config/rest/adaptor/utils/RestTemplateFactoryTest.java @@ -0,0 +1,32 @@ +package org.onap.ccsdk.config.rest.adaptor.utils; + +import org.onap.ccsdk.config.rest.adaptor.ConfigRestAdaptorConstants; +import org.onap.ccsdk.config.rest.adaptor.ConfigRestAdaptorException; +import org.onap.ccsdk.config.rest.adaptor.service.ConfigRestAdaptorServiceImpl; + +@SuppressWarnings("squid:S2187") +public class RestTemplateFactoryTest { + + public static void main(String[] args) { + + String propertyFile = RestTemplateFactoryTest.class.getClassLoader().getResource(".").getPath(); + System.out.println(" Property : " + propertyFile); + + try { + ConfigRestAdaptorServiceImpl configRestAdaptorServiceImpl = new ConfigRestAdaptorServiceImpl(propertyFile); + String restconfResponse = genericRestGetMDSALOperation(args, configRestAdaptorServiceImpl); + System.out.println("RestTemplateFactoryTest.main Completed with response :" + restconfResponse); + } catch (ConfigRestAdaptorException e) { + e.printStackTrace(); + } + } + + public static String genericRestGetMDSALOperation(String[] args, + ConfigRestAdaptorServiceImpl configRestAdaptorServiceImpl) throws ConfigRestAdaptorException { + String path = "config/Dummy-API:services/service-list/dummy-1234"; + String restconfResponse = configRestAdaptorServiceImpl.getResource(ConfigRestAdaptorConstants.SELECTOR_RESTCONF, + path, String.class); + return restconfResponse; + } + +} diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/config-rest-adaptor.properties b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/config-rest-adaptor.properties new file mode 100644 index 000000000..725e48429 --- /dev/null +++ b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/config-rest-adaptor.properties @@ -0,0 +1,29 @@ +# +# Configuration file for SDNC Controller Module +# + +org.onap.ccsdk.config.rest.adaptors.envtype=solo + +# Config Generator Microservices +org.onap.ccsdk.config.rest.adaptors.modelservice.type=generic +org.onap.ccsdk.config.rest.adaptors.modelservice.enable=true +org.onap.ccsdk.config.rest.adaptors.modelservice.url=http://localhost:8080/configgenerator/service/ +org.onap.ccsdk.config.rest.adaptors.modelservice.user=admin +org.onap.ccsdk.config.rest.adaptors.modelservice.passwd=admin + +# Generic RESTCONF Adaptor +org.onap.ccsdk.config.rest.adaptors.restconf.type=generic +org.onap.ccsdk.config.rest.adaptors.restconf.enable=true +org.onap.ccsdk.config.rest.adaptors.restconf.user=admin +org.onap.ccsdk.config.rest.adaptors.restconf.passwd=admin +org.onap.ccsdk.config.rest.adaptors.restconf.url=http://localhost:8181/restconf/ + +# SSL AAI Adaptor +org.onap.ccsdk.config.rest.adaptors.aai.propertyfile=aai.properties +org.onap.ccsdk.config.rest.adaptors.aai.type=ssl +org.onap.ccsdk.config.rest.adaptors.aai.enable=true +org.onap.ccsdk.config.rest.adaptors.aai.url=https://localhost:8443/onap-aai/ +org.onap.ccsdk.config.rest.adaptors.aai.ssl.trust=src/test/resources/truststore.client.jks +org.onap.ccsdk.config.rest.adaptors.aai.ssl.trust.psswd=changeme +org.onap.ccsdk.config.rest.adaptors.aai.ssl.key=src/test/resources/keystore.client.p12 +org.onap.ccsdk.config.rest.adaptors.aai.ssl.key.psswd=changeme diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/keystore.client.p12 b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/keystore.client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..c28c8cb79d5b60ca9691b388cd57531b940d1c4b GIT binary patch literal 1456 zcmV;h1yA}gf(5Js0Ru3C1!e{bDuzgg_YDCD0ic2fR|J9uQ!s)BPcVW7O9lxlhDe6@ z4FLxRpn?TFFoFd*0s#Opf(0=K2`Yw2hW8Bt2LUiB1_~;MNQU1Dvw1dp{9Y!)%8= zl3;YsIly}uHskm{a491`*Vwo^6!*&ox#l4AV)sZ93I*C0&c0>tw6Krh1#MJYki{lh zAW+_Nr?2H?Szr3nAP7)6za_4H4H)3V0C0i(8t@hy$6QM~Sz^Xn{3UZ`Xf=d1vRsXa&*dR z`Rmu&6!ArNqM^p?i{?l|aY6UOEZJzzCHZgkDX6O3YT`R0y2_sz89@G3LQ<#@Ij|{Z zUr~W*nc8JA^8aW9ngr;ZT4iJ(&DR=l=tUC2#FjDP4T7W7IP`)wQ&QonW4?+X9!G%# zOl0|L44PDy9n43R#^tmnRJC+y5W??Sj1P)(A2%w%x_y7eVA|d1%SxI~n4V}m4UTpe zOi;I}1xm;>^WJ|R-f4T(4b>_nT!PU{yIzI69Z7?7aK`G`pxE>Pp5OY~)Fnp~gss)R zwy|U@?eDvErflBq`v+mX%Ls&6ue7zVFyIN$Ms+KE)XF64D!(QSlapXC`tiL%N)o6r z`OI$`vz&a(m6NtM-VfuXLn%EOif$PBtsn`hJ)KFYzf@PdZtQeI6`{6iUlC~7Rmu=| z04?OxTi7En#3|Rthlyn;oba%Fm4{gcH!phQm5!+O@1bj-t}PWBZ$xC(-u%nCYPJ!O zm5Q)I!2}3Z>{&r-E^r5H)C71pzlm@Pv;@kcn(0*e`&lXe9n*tGkDH1AnL;~nei)YU z`x25nJ^n-%JIXEI522suVM%$9#d`I_rJ0f?5z8+Ru=_Y5xvx(cBl=TuEps)C!$dv!dFO7MsgQeUweVFK>Ksd5!%SpY9wM zS+Is$CBl}=68(L90G7*?QHg_MMgW^zxPNy|yBzpkCqMaK0)Vp5st)K5hMTvB{@;9$kB>CdVs9}5{+gbf%}ILjop2mkikY;ALbrlPX~ zK{JMC?=(?oXCLYi(YH$Gvt1uJ^Q>I!7orM0+)z1*y1>GW+^PTBSXt197HB5f?a|?4 z3wd!`o(6z`d?>F;DkG*1LMqewG(EJZ-RBnKhj4 zyE?Jl%jr|`vQvw}xFSA@j;VElzfA@|LaoT769o(HvCYJ%HhLB<Sy;NQ~+WRB20HjfClYyM}OB-qI2@4q=h-Jr6d6H2K zi*zb?C+yTb2}1`vqnv?TZ(b1p(j4tAEES25SKKD}D!X5s=iP_{7pk^2Azng~Fg-9K zFbM_)D-Ht!8U+9Z6sD>tkRCt!ug-ek%3H=tV?*DvHUt!oIO!fU@|+Vn7nV{jqbW!C KXQ%uE0t5hM+_voi literal 0 HcmV?d00001 diff --git a/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/truststore.client.jks b/blueprints-processor/adaptors/rest-adaptor-provider/src/test/resources/truststore.client.jks new file mode 100644 index 0000000000000000000000000000000000000000..d38a5e57da58c18771c44cb55b56b3d19f7a2d8a GIT binary patch literal 1228 zcmezO_TO6u1_mY|W(3o0$vK&+c_lz@>g=uO+Zb3Q^h^yb85mf44VqXw4Vst>7cet1 zGBI&7G@q(5x--96(}0(aQ>)FR?K>|cBR4C9LE~COZUas>=1>+kVW!Yv7>9$0$AOI5N;^Fi!NX>I~_V*2dDdgtiD$dL;$Vp8}&CNH| zGSC1iWad$XD^x&8D+H$&m8BLbI6Ept7#gY@sKVXF$tWg)Q0I^jv?Vn;Kes?HzbM^6 zPMp`!%+S!-z|hp#!q_qj$Tdge8Z_>Nd4W3q2?qPe1>_%119gah6cQ6N^%4s*^$@Pq zLvf{{iGdNacT|BIbTjgcOC0d&GH7B{LXI9rRtDxKMt%mMI2ThBBO}8>t_#x-n5?zs z?2FPd+W%X}BlxNFqWZ=*o0oQXE_58VeQLEpI`IG8uTwwyXIvAN=!$x_<=z)NesP=P zj51g0_78hlI$lg$@#C7iaMlKv;*>+?>lKf31Vpww#w;`ZJHfpxEa-63)2dJYGj|@E zYo;~DtZwTRyC->xY={1wI{3d-Y*Wei2aU$EeshzS-DBw0uxbzpmgIPze&dAPhG~rd zeV?BB5OmGpK5LO$thg|{RRZ%HS&p4w3R+AnwoW;ymm9Q{(@gf)obu2UbNO#}+*R4V zu_auMW%1^#8bv4GH?m!7v_8m|>$ghtr(ZYoh6~Z^A9kE7+IZplVgFrvMVA{w+_mEG zGBGnUFfMMKYtT5$fCm^Avcimv|5=y}7!3G8Jbn<5g_((Yhk-1J&&MLhB4T9dyhb>w zj$_TKOUr9C0-SHLO*>@}4U$%12{8yV2w-CYG8qg!4BXf_wAmP0S=pJG46LLqyi-#P z6cWo5D;3g;@^clEGct2hic<6R6cj=-GK&?GQ;SM6(=wA2OHvhp+`Rk}g+zst{7OCK z7zAc8U<@)c%=0q3b+yx4XOkk2Zdk{aV}IQunV%mnZ3;ZJa%UZrn&5SPVVAYJ@oapz zXZ6~!+-$V@l@+xq=k&~IHdc*q9-3j=8=q>&pN_hyC@rY_v%d2L)1H}Am?hO*)V$xT z>L1ACdUt*gKg++l`PX+Q`@bl!Ur{boEl|brs79Ona%q$ylf&HjQyU%}nEm(Psr;Cw z)xSTmoC^qa?&X*#EmUo~uk0^RrZ2CP?*a7>mI6{CvqHV=dG@!eds@!Eu~^RT!n9)f zyJp7(Z&qEBT6B1>_3;C*idwfH^-G&GS(|O4cJryr6XW$I@)?!HV-Fs1+S6V2z~rvv qt&aOeGh1%Uq-Cyexb?dCmD<#^W`z=*o&qu@zt$wDx+Pyd9svN3NU6vG literal 0 HcmV?d00001 -- 2.16.6