d7416497c9443e6bdc512b4d3fcf91f9d16e93b6
[federation.git] / gateway / src / test / java / org / acumos / federation / gateway / GatewayControllerTest.java
1 /*-
2  * ===============LICENSE_START=======================================================
3  * Acumos
4  * ===================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property & Tech Mahindra. All rights reserved.
6  * ===================================================================================
7  * This Acumos software file is distributed by AT&T and Tech Mahindra
8  * under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * This file is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ===============LICENSE_END=========================================================
19  */
20 package org.acumos.federation.gateway;
21
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.TimeUnit;
24 import java.util.function.Consumer;
25
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.fail;
30 import org.junit.Test;
31 import org.junit.Before;
32 import org.junit.runner.RunWith;
33
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.when;
36 import static org.mockito.Mockito.any;
37
38 import org.springframework.beans.factory.annotation.Autowired;
39 import org.springframework.boot.web.server.LocalServerPort;
40 import org.springframework.boot.test.context.SpringBootTest;
41 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
42 import org.springframework.boot.test.mock.mockito.MockBean;
43 import org.springframework.core.ParameterizedTypeReference;
44 import org.springframework.http.HttpMethod;
45 import org.springframework.test.context.junit4.SpringRunner;
46 import org.springframework.test.context.ContextConfiguration;
47 import org.springframework.web.client.HttpClientErrorException.Forbidden;
48 import org.springframework.web.client.HttpClientErrorException.NotFound;
49
50 import org.acumos.cds.client.ICommonDataServiceRestClient;
51 import org.acumos.cds.client.CommonDataServiceRestClientImpl;
52
53 import org.acumos.securityverification.service.ISecurityVerificationClientService;
54
55 import org.acumos.federation.client.FederationClient;
56 import org.acumos.federation.client.GatewayClient;
57 import org.acumos.federation.client.ClientBase;
58 import org.acumos.federation.client.config.ClientConfig;
59 import org.acumos.federation.client.config.BasicAuthConfig;
60 import org.acumos.federation.client.config.TlsConfig;
61
62 import org.acumos.federation.client.test.ClientMocking;
63 import static org.acumos.federation.client.test.ClientMocking.getConfig;
64 import static org.acumos.federation.client.test.ClientMocking.xq;
65
66 @RunWith(SpringRunner.class)
67 @ContextConfiguration(classes=GatewayServer.class)
68 @SpringBootTest(
69     classes = Application.class,
70     webEnvironment = WebEnvironment.RANDOM_PORT,
71     properties = {
72         "spring.main.allow-bean-definition-overriding=true",
73         "local.ssl.key-store=classpath:acumosa.pkcs12",
74         "local.ssl.key-store-password=acumosa",
75         "local.ssl.key-store-type=PKCS12",
76         "local.ssl.trust-store=classpath:acumosTrustStore.jks",
77         "local.ssl.trust-store-password=acumos",
78         "nexus.group-id=nxsgrpid",
79         "nexus.name-separator=,",
80         "docker.registry-url=someregistry:9999",
81         "federation.operator=defuserid"
82     }
83 )
84 public class GatewayControllerTest {
85         @LocalServerPort
86         private int port;
87
88         @Autowired
89         private ServerConfig local;
90
91         @MockBean
92         private Clients clients;
93
94         private CountDownLatch steps;
95
96         private final Consumer<ClientMocking.RequestInfo> count = x -> this.steps.countDown();
97
98         private SimulatedDockerClient docker;
99
100         static ClientConfig anonConfig() {
101                 ClientConfig ret = getConfig("bogus");
102                 ret.getSsl().setKeyStore(null);
103                 ret.setCreds(null);
104                 return ret;
105         }
106
107         private static class RawAnonClient extends ClientBase {
108                 public RawAnonClient(String url) throws Exception {
109                         super(url, anonConfig(), null, null);
110                 }
111
112                 public byte[] get(String uri) {
113                         return handle(uri, HttpMethod.GET, new ParameterizedTypeReference<byte[]>(){});
114                 }
115         }
116
117         @Before
118         public void init() throws Exception {
119                 ICommonDataServiceRestClient cdsClient = CommonDataServiceRestClientImpl.getInstance("http://cds:999", ClientBase.buildRestTemplate("http://cds:999", new ClientConfig(), null, null));
120
121                 (new ClientMocking())
122                     .on("GET /peer/search?self=true&subjectName=gateway.acumosa.org&_j=a&page=0&size=100", xq("{ 'content': [ {'peerId': '1', 'subjectName': 'gateway.acumosa.org', 'statusCode': 'AC', 'self': true } ], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 1 }"))
123                     .on("GET /peer/somepeer", xq("{ 'peerId': 'somepeer', 'apiUrl': 'https://somepeer.org:999'}"))
124                     .on("GET /peer/unknownpeer", "")
125                     .on("GET /peer/search?subjectName=gateway.acumosa.org&_j=a&page=0&size=100", xq("{ 'content': [ {'peerId': 'acumosa', 'subjectName': 'gateway.acumosa.org', 'statusCode': 'AC', 'self': true } ], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 1 }"))
126                     .on("GET /peer/search?subjectName=gateway.acumosb.org&_j=a&page=0&size=100", xq("{ 'content': [ {'peerId': 'acumosb', 'subjectName': 'gateway.acumosb.org', 'statusCode': 'AC', 'self': false } ], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 1 }"))
127                     .on("GET /peer/search?subjectName=gateway.acumosc.org&_j=a&page=0&size=100", xq("{ 'content': [ ], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 0 }"))
128                     .on("GET /peer/sub/999", xq("{ 'subId': 999, 'peerId': 'somepeer', 'selector': '{ \\'catalogId\\': \\'somecatalog\\' } ', 'userId': 'someUser' }"))
129                     .on("GET /peer/sub/998", "")
130                     .on("GET /peer/sub/997", xq("{ 'subId': 997, 'peerId': 'someotherpeer' }"))
131                     .on("GET /peer/sub/992", xq("{ 'subId': 992, 'peerId': 'somepeer', 'selector': '{ \\'catalogId\\': [ \\'firstcatalog\\', \\'secondcatalog\\' ] }', 'refreshInterval': 3600, 'userId': 'someUser' }"))
132                     .on("GET /peer/sub/993", xq("{ 'subId': 993, 'peerId': 'somepeer', 'selector': '{ \\'catalogId\\': true }', 'refreshInterval': 3600, 'userId': 'someUser' }"))
133                     .on("GET /peer/sub/994", xq("{ 'subId': 994, 'peerId': 'somepeer', 'selector': '{ \\'catalogId\\': [ \\'x\\', true ] }', 'refreshInterval': 3600, 'userId': 'someUser' }"))
134                     .on("GET /peer/sub/995", xq("{ 'subId': 995, 'peerId': 'somepeer', 'selector': '}', 'refreshInterval': 3600, 'userId': 'someUser' }"))
135                     .on("GET /peer/sub/996", xq("{ 'subId': 996, 'peerId': 'somepeer', 'selector': '{}', 'refreshInterval': 3600, 'userId': 'someUser' }"))
136                     .on("PUT /peer/sub/999", "", count)
137                     .on("GET /catalog/solution?ctlg=somecatalog&page=0&size=100", xq("{ 'content': [], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 0 }"))
138                     .on("GET /catalog?page=0&size=100", xq("{ 'content': [], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 0 }"))
139                     .on("POST /catalog", "{}", count)
140                     .on("GET /solution/somesolution", "")
141                     .on("GET /solution/somesolution/revision", "[]")
142                     .on("POST /solution", "{}", count)
143                     .on("POST /catalog/somecatalog/solution/somesolution", "", count)
144                     .on("PUT /solution/somesolution/pic", "", count)
145                     .on("GET /solution/ignored/revision/revid1", "")
146                     .on("POST /solution/somesolution/revision", xq("{ 'solutionId': 'somesolution', 'revisionId': 'revid1' }"), count)
147                     .on("POST /revision/revid1/catalog/somecatalog/descr", "", count)
148                     .on("GET /artifact/artid1", "")
149                     .on("POST /artifact", "", count)
150                     .on("POST /revision/revid1/artifact/artid1", "", count)
151                     .on("GET /document/docid1", "")
152                     .on("POST /document", "", count)
153                     .on("POST /revision/revid1/catalog/somecatalog/document/docid1", "", count)
154                     .on("GET /catalog/solution?ctlg=firstcatalog&page=0&size=100", xq("{ 'content': [ ], 'last': true, 'number': 0, 'size': 100, 'numberOfElements': 0 }"))
155                     .on("GET /catalog/solution?ctlg=secondcatalog&page=0&size=100", xq("{ 'content': [ { 'solutionId': 'cat2soln' } ], 'last': true, 'number': 1, 'size': 100, 'numberOfElements': 1 }"))
156                     .on("GET /solution/cat2soln", xq("{ 'solutionId': 'cat2soln' }"))
157                     .on("GET /solution/cat2soln/revision", xq("[ { 'revisionId': 'cat2rev', 'solutionId': 'cat2sol' }, { 'revisionId': 'cat2rev2', 'solutionId': 'cat2sol' } ]"))
158                     .on("GET /solution/cat2soln/pic", "asdf")
159                     .on("GET /solution/ignored/revision/cat2rev", xq("{ 'revisionId': 'cat2rev', 'solutionId': 'cat2sol' }"))
160                     .on("GET /solution/ignored/revision/cat2rev2", xq("{ 'revisionId': 'cat2rev2', 'solutionId': 'cat2sol' }"))
161                     .on("GET /revision/cat2rev/artifact", xq("[ { 'artifactId': 'artid2', 'filename': 'artfile2.arttype', 'version': 'artversionA' } ]"))
162                     .on("GET /revision/cat2rev2/artifact", "[]")
163                     .on("GET /revision/cat2rev/catalog/secondcatalog/descr", xq("{ 'revisionId': 'cat2rev', 'catalogId': 'secondcatalog', 'description': 'old description' }"))
164                     .on("GET /revision/cat2rev2/catalog/secondcatalog/descr", xq("{ 'revisionId': 'cat2rev2', 'catalogId': 'secondcatalog', 'description': 'description A' }"))
165                     .on("GET /revision/cat2rev/catalog/secondcatalog/document", "[]")
166                     .on("GET /document/docid2", xq("{ 'documentId': 'docid2', 'filename': 'docfile2.doctype', 'version': 'docversionA' }"))
167                     .on("GET /artifact/artid2", xq("{ 'artifactId': 'artid2', 'filename': 'artfile2.arttype', 'version': 'artversionA' }"))
168                     .on("GET /revision/cat2rev2/catalog/secondcatalog/document", "[]")
169                     .on("DELETE /revision/cat2rev/catalog/secondcatalog/descr", "")
170                     .on("PUT /artifact/artid2", "")
171                     .on("PUT /document/docid2", "")
172                     .on("POST /revision/cat2rev/catalog/secondcatalog/document/docid2", "")
173                     .on("PUT /solution/cat2soln/revision/cat2rev", "")
174                     .on("PUT /revision/cat2rev2/catalog/secondcatalog/descr", "")
175                     .on("POST /solution/cat2soln/tag/tag1", "")
176                     .on("PUT /solution/cat2soln", "")
177                     .on("PUT /peer/sub/992", "", count)
178                     .on("POST /notif", xq("{ 'notificationId': 'noteid' }"))
179                     .on("POST /notif/noteid/user/someUser", "")
180                     .applyTo(cdsClient);
181                 when(clients.getCDSClient()).thenReturn(cdsClient);
182
183                 NexusClient nexusClient = new NexusClient("https://nexus:999", new ClientConfig());
184                 (new ClientMocking())
185                     .on("PUT /nxsgrpid,somesolution/somefile/someversion/somefile-someversion.type", "")
186                     .on("PUT /nxsgrpid,somesolution/docfile/na/docfile-na.doctype", "")
187                     .on("PUT /nxsgrpid,cat2soln/docfile2/na/docfile2-na.doctype", "")
188                     .on("PUT /nxsgrpid,cat2soln/artfile2/artversion2B/artfile2-artversion2B.arttype", "")
189                     .applyTo(nexusClient);
190                 when(clients.getNexusClient()).thenReturn(nexusClient);
191
192                 FederationClient fedClient = new FederationClient("https://peer:999", new ClientConfig());
193                 (new ClientMocking())
194                     .on("GET /catalogs", xq("{ 'content': [ {}, {} ]}"))
195                     .on("GET /ping", xq("{ 'content': {}}"))
196                     .on("GET /peers", xq("{ 'content': [{}, {}]}"))
197                     .on("POST /peer/register", xq("{ 'content': {}}"))
198                     .on("GET /solutions?catalogId=somecatalog", xq("{ 'content': [ { 'solutionId': 'somesolution' } ]}"))
199                     .on("GET /solutions/somesolution", xq("{ 'content': { 'picture': 'YXNkZg==', 'revisions': [ { 'revisionId': 'revid1' } ] }}"))
200                     .on("GET /solutions/somesolution/revisions/revid1?catalogId=somecatalog", xq("{ 'content': { 'solutionId': 'somesolution', 'revisionId': 'revid1', 'documents': [ { 'documentId': 'docid1', 'filename': 'docfile.doctype', 'version': 'docversion' } ], 'artifacts': [ { 'artifactId': 'artid1', 'name': 'somename', 'filename': 'someimage', 'version': 'someversion', 'artifactTypeCode': 'DI', 'description': 'thisimage:thistag' } ], 'revCatDescription': { 'revisionId': 'revid1', 'catalogId': 'somecatalog', 'description': 'some description' }}}"))
201                     .on("GET /artifacts/artid1/content", "Artifact Content")
202                     .on("GET /artifacts/artid2/content", "Artifact Content 2")
203                     .on("GET /documents/docid1/content", "Document Content")
204                     .on("GET /documents/docid2/content", "Document Content 2")
205                     .on("GET /solutions?catalogId=firstcatalog", xq("{ 'content': [ ]}"))
206                     .on("GET /solutions?catalogId=secondcatalog", xq("{ 'content': [ { 'solutionId': 'cat2soln' } ]}"))
207                     .on("GET /solutions/cat2soln", xq("{ 'content': { 'solutionId': 'cat2soln', 'picture': 'YXNkZg==', 'revisions': [ { 'revisionId': 'cat2rev' }, { 'revisionId': 'cat2rev2' } ], 'tags': [ { 'tag': 'tag1' } ] }}"))
208                     .on("GET /solutions/cat2soln/revisions/cat2rev?catalogId=secondcatalog", xq("{ 'content': { 'solutionId': 'cat2soln', 'revisionId': 'cat2rev', 'documents': [ { 'documentId': 'docid2', 'filename': 'docfile2.doctype', 'version': 'docversionB' } ], 'artifacts': [ { 'artifactId': 'artid2', 'filename': 'artfile2.arttype', 'version': 'artversion2B' } ] }}"))
209                     .on("GET /solutions/cat2soln/revisions/cat2rev2?catalogId=secondcatalog", xq("{ 'content': { 'solutionId': 'cat2soln', 'revisionId': 'cat2rev2', 'revCatDescription': { 'catalogId': 'secondcatalog', 'revisionId': 'cat2rev2', 'description': 'description B' }, 'documents': [  ], 'artifacts': [ ] }}"))
210                     .applyTo(fedClient);
211                 when(clients.getFederationClient(any(String.class))).thenReturn(fedClient);
212
213                 docker = new SimulatedDockerClient();
214                 when(clients.getDockerClient()).thenReturn(docker.getClient());
215
216                 ISecurityVerificationClientService sv = mock(ISecurityVerificationClientService.class);
217                 when (clients.getSVClient()).thenReturn(sv);
218         }
219
220         @Test
221         public void testConfig() throws Exception {
222                 assertEquals("acumosa", local.getSsl().getKeyStorePassword());
223
224                 GatewayClient self = new GatewayClient("https://localhost:" + port, getConfig("acumosa"));
225                 GatewayClient known = new GatewayClient("https://localhost:" + port, getConfig("acumosb"));
226                 GatewayClient unknown = new GatewayClient("https://localhost:" + port, getConfig("acumosc"));
227                 assertNotNull(self.ping("somepeer"));
228                 assertNotNull(self.register("somepeer"));
229                 assertNotNull(self.getPeers("somepeer"));
230                 try {
231                         known.ping("somepeer");
232                         fail();
233                 } catch (Forbidden ux) {
234                         // expected case
235                 }
236                 try {
237                         unknown.ping("somepeer");
238                         fail();
239                 } catch (Forbidden ux) {
240                         // expected case
241                 }
242                 try {
243                         self.ping("unknownpeer");
244                         fail();
245                 } catch (NotFound nf) {
246                         // expected case
247                 }
248                 assertEquals(2, self.getCatalogs("somepeer").size());
249                 assertEquals(1, self.getSolutions("somepeer", "somecatalog").size());
250                 assertNotNull(self.getSolution("somepeer", "somesolution"));
251                 try {
252                         self.triggerPeerSubscription("unknownpeer", 999);
253                         fail();
254                 } catch (NotFound nf) {
255                         // expected case
256                 }
257                 try {
258                         self.triggerPeerSubscription("somepeer", 998);
259                         fail();
260                 } catch (NotFound nf) {
261                         // expected case
262                 }
263                 try {
264                         self.triggerPeerSubscription("somepeer", 997);
265                         fail();
266                 } catch (NotFound nf) {
267                         // expected case
268                 }
269                 docker.clearImages();
270                 docker.addImage("imageid1", "tagA:1", "tagB:2");
271                 docker.addImage("imageid2", "tagX:1", "thisimage:thistag");
272                 steps = new CountDownLatch(12 + 1);
273                 self.triggerPeerSubscription("somepeer", 992);
274                 self.triggerPeerSubscription("somepeer", 993);
275                 self.triggerPeerSubscription("somepeer", 994);
276                 self.triggerPeerSubscription("somepeer", 995);
277                 self.triggerPeerSubscription("somepeer", 996);
278                 self.triggerPeerSubscription("somepeer", 999);
279                 steps.await(2, TimeUnit.SECONDS);
280                 assertEquals("Incomplete steps remain", 0, steps.getCount() - 1);
281         }
282
283
284         @Test
285         public void testSwagger() throws Exception {
286                 RawAnonClient rac = new RawAnonClient("https://localhost:" + port);
287                 assertNotNull(rac);
288                 rac.get("/swagger-ui.html");
289                 rac.get("/v2/api-docs");
290         }
291 }