forked from DeBortoliWines/openerp-java-api
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathSessionTest.java
More file actions
489 lines (420 loc) · 21.1 KB
/
Copy pathSessionTest.java
File metadata and controls
489 lines (420 loc) · 21.1 KB
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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
package com.odoojava.api;
import com.odoojava.api.Version;
import com.odoojava.api.Session;
import com.odoojava.api.Context;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.assertj.core.api.Assertions.fail;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import org.apache.xmlrpc.XmlRpcException;
import org.assertj.core.api.SoftAssertions;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockserver.integration.ClientAndProxy;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.RegexBody;
import org.mockserver.model.StringBody;
import org.mockserver.socket.PortFactory;
import org.mockserver.socket.SSLFactory;
import org.mockserver.verify.VerificationTimes;
import com.odoojava.api.DemoDbGetter.DemoDbInfoRequester;
import com.odoojava.api.OdooXmlRpcProxy.RPCProtocol;
public class SessionTest {
private static final String MOCKSERVER_HOST = "localhost";
private static final String ADMIN = "admin";
private static final String MOCK_DATABASE_NAME = "demo_90_1453880126";
private static final int MOCKSERVER_PORT = PortFactory.findFreePort();
private static final String WRONG_DB = "wrong_db";
private static final String WRONG_DB_MESSAGE_REGEXP = "[\\w\\W]*[Dd]atabase \"?" + WRONG_DB
+ "\"? does not exist[\\w\\W]*";
private static final String LOGIN_FAILED_MESSAGE = "Incorrect username and/or password. Login Failed.";
private static final String WRONG_PASSWORD = "wrong_password";
private static final String WRONG_USERNAME = "wrong_userName";
private static RPCProtocol protocol;
private static String host;
private static Integer port;
private static String databaseName;
private static String userName;
private static String password;
private Session session;
private static ClientAndProxy proxy;
private static ClientAndServer mockServer;
private static boolean useMockServer = true;
private static SSLSocketFactory previousFactory;
@BeforeClass
public static void startProxy() throws Exception {
if (isUsingMockServer()) {
previousFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(SSLFactory.getInstance().sslContext().getSocketFactory());
proxy = ClientAndProxy.startClientAndProxy(PortFactory.findFreePort());
mockServer = ClientAndServer.startClientAndServer(MOCKSERVER_PORT);
}
}
@Before
public void startMockServer() throws Exception {
if (isUsingMockServer()) {
mockServer.reset();
mockValidLogin();
mockGetContext();
}
}
private static void mockDeniedDatabaseListing() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/db")
.withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>list</methodName><params/></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<fault>\n<value><struct>\n<member>\n<name>faultCode</name>\n<value><int>3</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>Access denied</string></value>\n</member>\n</struct></value>\n</fault>\n</methodResponse>\n"));
}
private static void mockAllowedDatabaseListing() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/db")
.withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>list</methodName><params/></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value>\n<array>\n<data>\n<value><string>"
+ MOCK_DATABASE_NAME
+ "</string></value>\n</data>\n</array>\n</value>\n</param>\n</params>\n</methodResponse>\n"));
}
private static void mockValidLogin() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/common").withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>login</methodName><params><param><value>"
+ MOCK_DATABASE_NAME + "</value></param><param><value>" + ADMIN
+ "</value></param><param><value>" + ADMIN + "</value></param></params></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><int>1</int></value>\n</param>\n</params>\n</methodResponse>\n"));
}
private static void mockGetContext() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/object").withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>execute</methodName><params><param><value>"
+ MOCK_DATABASE_NAME
+ "</value></param><param><value><i4>1</i4></value></param><param><value>" + ADMIN
+ "</value></param><param><value>res.users</value></param><param><value>context_get</value></param></params></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><struct>\n<member>\n<name>lang</name>\n<value><string>en_US</string></value>\n</member>\n<member>\n<name>tz</name>\n<value><string>Europe/Brussels</string></value>\n</member>\n</struct></value>\n</param>\n</params>\n</methodResponse>\n"));
}
private static void mockLoginWrongUsername() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/common")
.withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>login</methodName><params><param><value>"
+ MOCK_DATABASE_NAME
+ "</value></param><param><value>wrong_userName</value></param><param><value>"
+ ADMIN + "</value></param></params></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><boolean>0</boolean></value>\n</param>\n</params>\n</methodResponse>\n"));
}
private static void mockLoginWrongPassword() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/common")
.withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>login</methodName><params><param><value>"
+ MOCK_DATABASE_NAME + "</value></param><param><value>" + ADMIN
+ "</value></param><param><value>wrong_password</value></param></params></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><boolean>0</boolean></value>\n</param>\n</params>\n</methodResponse>\n"));
}
private static void mockLoginWrongDb() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/common")
.withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>login</methodName><params><param><value>wrong_db</value></param><param><value>"
+ ADMIN + "</value></param><param><value>" + ADMIN
+ "</value></param></params></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<fault>\n<value><struct>\n<member>\n<name>faultCode</name>\n<value><int>1</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>Traceback (most recent call last):\n File \"/home/odoo/src/odoo/9.0/openerp/service/wsgi_server.py\", line 56, in xmlrpc_return\n result = openerp.http.dispatch_rpc(service, method, params)\n File \"/home/odoo/src/odoo/9.0/openerp/http.py\", line 114, in dispatch_rpc\n result = dispatch(method, params)\n File \"/home/odoo/src/odoo/9.0/openerp/service/common.py\", line 57, in dispatch\n return g[exp_method_name](*params)\n File \"/home/odoo/src/odoo/9.0/openerp/service/common.py\", line 23, in exp_login\n res = security.login(db, login, password)\n File \"/home/odoo/src/odoo/9.0/openerp/service/security.py\", line 8, in login\n res_users = openerp.registry(db)['res.users']\n File \"/home/odoo/src/odoo/9.0/openerp/__init__.py\", line 50, in registry\n return modules.registry.RegistryManager.get(database_name)\n File \"/home/odoo/src/odoo/9.0/openerp/modules/registry.py\", line 354, in get\n update_module)\n File \"/home/odoo/src/odoo/9.0/openerp/modules/registry.py\", line 371, in new\n registry = Registry(db_name)\n File \"/home/odoo/src/odoo/9.0/openerp/modules/registry.py\", line 63, in __init__\n cr = self.cursor()\n File \"/home/odoo/src/odoo/9.0/openerp/modules/registry.py\", line 278, in cursor\n return self._db.cursor()\n File \"/home/odoo/src/odoo/9.0/openerp/sql_db.py\", line 556, in cursor\n return Cursor(self.__pool, self.dbname, self.dsn, serialized=serialized)\n File \"/home/odoo/src/odoo/9.0/openerp/sql_db.py\", line 162, in __init__\n self._cnx = pool.borrow(dsn)\n File \"/home/odoo/src/odoo/9.0/openerp/sql_db.py\", line 445, in _locked\n return fun(self, *args, **kwargs)\n File \"/home/odoo/src/odoo/9.0/openerp/sql_db.py\", line 507, in borrow\n result = psycopg2.connect(dsn=dsn, connection_factory=PsycoConnection)\n File \"/usr/lib/python2.7/dist-packages/psycopg2/__init__.py\", line 179, in connect\n connection_factory=connection_factory, async=async)\nOperationalError: FATAL: database \"wrong_db\" does not exist\n\n</string></value>\n</member>\n</struct></value>\n</fault>\n</methodResponse>\n"));
}
private static void mockServerVersionResponse() {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/db")
.withBody(new StringBody(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><methodCall><methodName>server_version</methodName><params/></methodCall>")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><string>9.0e</string></value>\n</param>\n</params>\n</methodResponse>\n"));
}
@AfterClass
public static void stopProxy() throws Exception {
if (isUsingMockServer()) {
mockServer.stop();
proxy.dumpToLogAsJava();
proxy.stop();
HttpsURLConnection.setDefaultSSLSocketFactory(previousFactory);
}
}
/*
* Initialize connection data to demo db only once per test series
*/
@BeforeClass
public static void requestDemoDb() throws IOException, XmlRpcException {
if (isUsingMockServer()) {
SessionTest.protocol = RPCProtocol.RPC_HTTPS;
SessionTest.host = MOCKSERVER_HOST;
SessionTest.port = MOCKSERVER_PORT;
SessionTest.userName = ADMIN;
SessionTest.password = ADMIN;
SessionTest.databaseName = MOCK_DATABASE_NAME;
} else {
DemoDbGetter.getDemoDb(new DemoDbConnectionDataSetter());
}
}
@Before
public void setUp() throws Exception {
session = new Session(protocol, host, port, databaseName, userName, password);
}
@Test
public void should_throw_when_parameters_are_incorrect() throws Exception {
if (isUsingMockServer()) {
mockLoginWrongUsername();
mockLoginWrongPassword();
mockLoginWrongDb();
}
Session badSession = new Session(RPCProtocol.RPC_HTTPS, host, port, databaseName, WRONG_USERNAME, password);
Throwable thrown = catchThrowable(() -> badSession.startSession());
Session badSession2 = new Session(RPCProtocol.RPC_HTTPS, host, port, databaseName, userName, WRONG_PASSWORD);
Throwable thrown2 = catchThrowable(() -> badSession2.startSession());
Session badSession3 = new Session(RPCProtocol.RPC_HTTPS, host, port, WRONG_DB, userName, password);
Throwable thrown3 = catchThrowable(() -> badSession3.startSession());
Session badSession4 = new Session(RPCProtocol.RPC_HTTPS, host, 1234, databaseName, userName, password);
Throwable thrown4 = catchThrowable(() -> badSession4.startSession());
// use SoftAssertions instead of direct assertThat methods
// to collect all failing assertions in one go
SoftAssertions softly = new SoftAssertions();
softly.assertThat(thrown).as("Bad userName").isInstanceOf(Exception.class).hasMessage(LOGIN_FAILED_MESSAGE);
softly.assertThat(thrown2).as("Bad password").isInstanceOf(Exception.class).hasMessage(LOGIN_FAILED_MESSAGE);
softly.assertThat(thrown3).as("Bad db").isInstanceOf(XmlRpcException.class)
.hasMessageMatching(WRONG_DB_MESSAGE_REGEXP);
softly.assertThat(thrown4).as("Bad port").isInstanceOf(XmlRpcException.class);
// Don't forget to call SoftAssertions global verification !
softly.assertAll();
}
@Test
public void should_contain_tags_in_context_after_start() throws Exception {
// Need to be authenticated to be allowed to fetch the remote context
session.authenticate();
session.getRemoteContext();
assertThat(session.getContext()).as("Session context").containsEntry(Context.ActiveTestTag, true)
.containsKeys(Context.LangTag, Context.TimezoneTag);
}
@Test
public void should_provide_userId_after_authenticate() throws Exception {
int userId = session.authenticate();
assertThat(userId).as("User ID").isNotZero();
}
@Test
public void shoudl_return_database_list_or_throw() throws Exception {
// use SoftAssertions instead of direct assertThat methods
// to collect all failing assertions in one go
SoftAssertions softly = new SoftAssertions();
if (isUsingMockServer()) {
mockAllowedDatabaseListing();
}
try {
ArrayList<String> databaseList = Session.getDatabaseList(protocol, host, port);
softly.assertThat(databaseList).as("List of databases on server " + host).contains(databaseName);
} catch (XmlRpcException e) {
if (isUsingMockServer()) {
softly.assertThat(e).as("Database listing allowed, no exception should have been throwed").isNull();
} else {
// Listing is denied on demo server
softly.assertThat(e).as("XmlRpcException thrown when access is denied")
.isInstanceOf(XmlRpcException.class).hasMessage("Access denied");
}
}
if (isUsingMockServer()) {
mockServer.reset();
mockDeniedDatabaseListing();
}
Throwable thrown = catchThrowable(() -> Session.getDatabaseList(protocol, host, port));
softly.assertThat(thrown).as("XmlRpcException thrown when access is denied").isInstanceOf(XmlRpcException.class)
.hasMessage("Access denied");
// Don't forget to call SoftAssertions global verification !
softly.assertAll();
}
private static boolean isUsingMockServer() {
return useMockServer;
}
@Test
public void should_find_database_in_listing_or_throw() throws Exception {
// use SoftAssertions instead of direct assertThat methods
// to collect all failing assertions in one go
SoftAssertions softly = new SoftAssertions();
// Listing denied
if (isUsingMockServer()) {
mockDeniedDatabaseListing();
}
Throwable thrown = catchThrowable(() -> session.checkDatabasePresence());
softly.assertThat(thrown).as("Exception thrown when db listing disabled").isInstanceOf(Exception.class)
.hasMessage("Access denied");
// Listing not allowed on demo server
if (isUsingMockServer()) {
// Listing allowed and db found
mockServer.reset();
mockAllowedDatabaseListing();
thrown = catchThrowable(() -> session.checkDatabasePresence());
softly.assertThat(thrown).as("Exception thrown when db listing enabled and db should be found").isNull();
// Listing allowed and db not found
session = new Session(protocol, host, port, WRONG_DB, userName, password);
thrown = catchThrowable(() -> session.checkDatabasePresence());
softly.assertThat(thrown).as("Exception thrown when db listing enabled but db not found")
.isInstanceOf(Exception.class)
.hasMessage("Error while connecting to Odoo. Database [" + WRONG_DB
+ "] was not found in the following list: " + System.getProperty("line.separator")
+ System.getProperty("line.separator") + MOCK_DATABASE_NAME
+ System.getProperty("line.separator"));
}
// Don't forget to call SoftAssertions global verification !
softly.assertAll();
}
@Test
public void should_return_server_version() throws Exception {
if (isUsingMockServer()) {
mockServerVersionResponse();
}
Version version = session.getServerVersion();
assertThat(version).as("Server version").isNotNull();
}
static boolean checkedDatabasePresence = false, authenticated = false, fetchedRemoteContext = false;
@Test
public void should_authenticate_and_fetch_remote_context_on_session_start() throws Exception {
session = new Session(protocol, host, port, databaseName, userName, password) {
@Override
int authenticate() throws XmlRpcException, Exception {
authenticated = true;
return 1;
}
@Override
void checkDatabasePresenceSafe() {
checkedDatabasePresence = true;
}
@Override
void getRemoteContext() throws XmlRpcException {
fetchedRemoteContext = true;
}
};
session.startSession();
// use SoftAssertions instead of direct assertThat methods
// to collect all failing assertions in one go
SoftAssertions softly = new SoftAssertions();
softly.assertThat(checkedDatabasePresence).as("Checked for database presence").isTrue();
softly.assertThat(authenticated).as("Authenticated").isTrue();
softly.assertThat(fetchedRemoteContext).as("Fetched remote context").isTrue();
// Don't forget to call SoftAssertions global verification !
softly.assertAll();
}
@Test
public void should_send_given_parameters() throws Exception {
// Check parameters given are sent in order
if (isUsingMockServer()) {
mockServer
.when(request().withMethod("POST").withPath("/xmlrpc/2/object")
.withBody(new RegexBody(".*res.users.*context_get.*")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><int>1</int></value>\n</param>\n</params>\n</methodResponse>\n"));
} else {
session.startSession();
}
Object[] parameters = new Object[] { "parameter1" };
session.executeCommand("res.users", "context_get", parameters);
if (isUsingMockServer()) {
mockServer.verify(request().withBody(new RegexBody(".*parameter1.*")), VerificationTimes.once());
}
// Check empty array is working
parameters = new Object[] {};
session.executeCommand("res.users", "context_get", parameters);
if (isUsingMockServer()) {
mockServer.verify(request().withBody(new RegexBody(".*res.users.*context_get.*")),
VerificationTimes.exactly(2));
}
// Check null is working
parameters = null;
session.executeCommand("res.users", "context_get", parameters);
if (isUsingMockServer()) {
mockServer.verify(request().withBody(new RegexBody(".*res.users.*context_get.*")),
VerificationTimes.exactly(3));
}
}
@Test
public void should_call_method_execute_on_executeCommand() throws Exception {
if (isUsingMockServer()) {
mockServer
.when(request().withPath("/xmlrpc/2/object")
.withBody(RegexBody.regex(".*<methodName>execute</methodName>.*")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><int>1</int></value>\n</param>\n</params>\n</methodResponse>\n"));
} else {
session.startSession();
}
session.executeCommand("res.users", "context_get", null);
if (isUsingMockServer()) {
mockServer.verify(request().withBody(new RegexBody(".*<methodName>execute</methodName>.*")),
VerificationTimes.once());
}
}
@Test
public void should_call_method_exec_workflow_on_executeCommand() throws Exception {
if (isUsingMockServer()) {
mockServer
.when(request().withPath("/xmlrpc/2/object")
.withBody(RegexBody.regex(".*<methodName>exec_workflow</methodName>.*")))
.respond(response().withStatusCode(200).withBody(
"<?xml version='1.0'?>\n<methodResponse>\n<params>\n<param>\n<value><int>1</int></value>\n</param>\n</params>\n</methodResponse>\n"));
} else {
session.startSession();
}
session.executeWorkflow("account.invoice", "invoice_open", 42424242);
if (isUsingMockServer()) {
mockServer.verify(request().withBody(new RegexBody(".*<methodName>exec_workflow</methodName>.*")),
VerificationTimes.once());
}
}
@Test
public void should_default_protocol_to_http() throws Exception {
session = new Session(host, port, databaseName, userName, password);
Field protocolField = Session.class.getDeclaredField("protocol");
if (protocolField != null) {
protocolField.setAccessible(true);
} else {
fail("protocol field not found in class " + Session.class.getName());
}
assertThat(protocolField.get(session)).isEqualTo(RPCProtocol.RPC_HTTP);
}
/**
* Only used to set the static data for connection on the main class
*/
private static class DemoDbConnectionDataSetter implements DemoDbInfoRequester {
@Override
public void setProtocol(RPCProtocol protocol) {
SessionTest.protocol = protocol;
}
@Override
public void setHost(String host) {
SessionTest.host = host;
}
@Override
public void setPort(Integer port) {
SessionTest.port = port;
}
@Override
public void setDatabaseName(String databaseName) {
SessionTest.databaseName = databaseName;
}
@Override
public void setUserName(String userName) {
SessionTest.userName = userName;
}
@Override
public void setPassword(String password) {
SessionTest.password = password;
}
}
}