How to mock an s3ObjectStream - testing

I have code to test as follows:
#BeforeEach
static void setup() throws IOException {
Context context = new MockLambdaContext();
mockAmazonS3Client();
}
#Test
#DisplayName("Testing handleRequest returns complete when middle base64 piece")
public void handleRequestTestMiddlePiece() throws IOException {
ApplicationHandler applicationHandler = new ApplicationHandler();
mockAmazonS3Client();
input.replace("tag","middle");
assertEquals("complete", applicationHandler.handleRequest(input, context));
}
public static void mockAmazonS3Client() throws IOException {
AmazonS3Client mockAmazonS3Client = mock(AmazonS3Client.class);
S3Object s3Object = mock(S3Object.class);
S3ObjectInputStream s3ObjectInputStream = mock(S3ObjectInputStream.class);
InputStream testInputStream = new ByteArrayInputStream("Test".getBytes());
when(mockAmazonS3Client.getObject(any(String.class), any(String.class))).thenAnswer(invocationOnMock -> {
return s3Object;
});
when(s3ObjectInputStream.read(any(byte[].class))).thenAnswer(invocation -> {
return testInputStream.read(invocation.getArgument(0));
});
when(s3Object.getObjectContent()).thenReturn(s3ObjectInputStream);
new MockUp<AmazonS3Client>() {
#Mock
public S3Object getObject(String bucketName, String key) throws SdkClientException, AmazonServiceException {
return s3Object;
}
};
new MockUp<AmazonS3Client>() {
#Mock
PutObjectResult putObject(PutObjectRequest var1) throws SdkClientException, AmazonServiceException {
return null;
}
};
new MockUp<AmazonS3Client>() {
#Mock
public S3ObjectInputStream getObjectContent() {
return s3ObjectInputStream;
}
};
}
In the mockAmazonS3Client I have mocked the S3Object and the S3ObjectStream that getObject produces using mockito. I have managed to use a 'when' to have getObjectContent call an S3ObjectStream. However, when I use another 'when' to mock what happens if the S3ObjectSteam is read it throws an exception where 0 bytes are returned:
java.io.IOException: Underlying input stream returned zero bytes
It could be the second 'when' that is failing for some reason, though I'm not sure. Any thoughts will be gratefully received. Thanks.

Related

Trouble in testing with invalid field

public with sharing class SobjectByParams {
public SObject createSObject(String sObjectName, Map<String, String> fields) {
String invalidSObjectError = System.Label.invalid_Sobject_Name;
String invalidFieldError = System.Label.Invalid_Sobject_Field;
SObject newObject;
try {
newObject = (SObject) Type.forName(sObjectName).newInstance();
} catch (NullPointerException ex) {
throw new InvalidTypeNameException(invalidSObjectError);
}
for (String field : fields.keySet()) {
try {
newObject.put(field, fields.get(field));
} catch (SObjectException ex) {
throw new InvalidTypeNameException(invalidFieldError);
}
}
insert newObject;
return newObject;
}
public class InvalidTypeNameException extends Exception {
}
}
#IsTest
public with sharing class SobjectByParamsTest {
SobjectByParams sobjectByParams;
private static final String TestName = 'TestName';
private static final String BCity = 'Lviv';
private static final String LastName = 'Kapo';
private static final String Email = 'email';
#IsTest
static void createSObject() {
SobjectByParams sobjectByParams = new SobjectByParams();
Map<String, String> fields = new Map<String, String>();
fields.put('BillingCity', BCity);
Test.startTest();
SObject result = sobjectByParams.createSObject(TestName, fields);
Test.stopTest();
System.assertEquals(BCity, result.get(BCity));
}
}
SobjectByParams.InvalidTypeNameException: invalidSobjectNameError - PROBLEM
TEST WORKING on 53.33% but I need min 80%
I don`t know how to fix my problem.
Shouldn't TestName be a sObject name like Contact? Does this code work when you run it normally, does it insert anything or throw? Check/fix that and then you probably need a negative test too.
Make second test method
#isTest
static void testPassingBadData() {
SobjectByParams sobjectByParams = new SobjectByParams();
Map<String, String> fields = new Map<String, String>();
fields.put('BillingCity', BCity);
try{
sobjectByParams.createSObject('NoSuchObjectInTheSystem', fields);
System.assert(false, 'This should have failed and thrown exception');
} catch(Exception e){
System.assert(e.getMessage().contains(Label.Invalid_Sobject_Field));
}
}

NPE when trying to use Jetty async HTTP client

When trying to use Firebase Cloud Messaging by Google with the help of non-blocking Jetty HTTP client in a simple test case that I have prepared at GitHub -
private static final HttpClient sHttpClient = new HttpClient();
private static final Response.ContentListener sFcmListener = new Response.ContentListener() {
#Override
public void onContent(Response response, ByteBuffer content) {
if (response.getStatus() != 200) {
return;
}
String body = StandardCharsets.UTF_8.decode(content).toString();
System.out.printf("onContent: %s\n", body);
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
try {
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map result = (Map) results[0];
String error = (String) result.get(FCM_ERROR);
if (FCM_NOT_REGISTERED.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ignore) {
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.onResponseContent(sFcmListener)
.send();
}
but unfortunately the execution fails immediately with NPE:
2017-06-30 10:46:41.312:INFO::main: Logging initialized #168ms to org.eclipse.jetty.util.log.StdErrLog
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NullPointerException
at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118)
at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:101)
at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:682)
at de.afarber.fcmnotregistered.Main.main(Main.java:68)
Caused by: java.lang.NullPointerException
at org.eclipse.jetty.io.ssl.SslClientConnectionFactory.newConnection(SslClientConnectionFactory.java:59)
at org.eclipse.jetty.client.AbstractHttpClientTransport$ClientSelectorManager.newConnection(AbstractHttpClientTransport.java:191)
at org.eclipse.jetty.io.ManagedSelector.createEndPoint(ManagedSelector.java:420)
at org.eclipse.jetty.io.ManagedSelector.access$1600(ManagedSelector.java:61)
at org.eclipse.jetty.io.ManagedSelector$CreateEndPoint.run(ManagedSelector.java:599)
at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:199)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
at java.lang.Thread.run(Thread.java:745)
Why does it happen please?
UPDATE:
I have switched to using BufferingResponseListener and the NPE is gone, but now the program prints java.net.NoRouteToHostException: No route to host even though the Google FCM endpoint is a well-known host:
private static final HttpClient sHttpClient = new HttpClient();
private static final BufferingResponseListener sFcmListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
System.err.println(result.getFailure()); // No route to host
return;
}
String body = getContentAsString(StandardCharsets.UTF_8);
System.out.printf("onContent: %s\n", body);
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
try {
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map map = (Map) results[0];
String error = (String) map.get(FCM_ERROR);
if (FCM_NOT_REGISTERED.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ex) {
System.err.println(ex);
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.send(sFcmListener);
}
I get the No route to host for any FCM_URL value I try, why?
Adding SslContextFactory has helped me:
private static final SslContextFactory sFactory = new SslContextFactory();
private static final HttpClient sHttpClient = new HttpClient(sFactory);
private static final BufferingResponseListener sFcmListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
System.err.println(result.getFailure());
return;
}
String body = getContentAsString(StandardCharsets.UTF_8);
System.out.printf("onComplete: %s\n", body);
try {
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map map = (Map) results[0];
String error = (String) map.get(FCM_ERROR);
System.out.printf("error: %s\n", error);
if (FCM_NOT_REGISTERED.equals(error) ||
FCM_MISSING_REGISTRATION.equals(error) ||
FCM_INVALID_REGISTRATION.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ex) {
System.err.println(ex);
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.send(sFcmListener);
}
The still open question I have is how to retrieve the invalid FCM token that I have used in the Jetty HTTP client request, so that I can delete it from my database on the response...

Spring jmsTemplate send Unit testing doen't work

My service method looks like below, I am trying to mock JmsTemplate so that it can send message during unit testing, but it doesn't execute jmsTemplate.send(...), it directly goes to next line, How can i execute jmsTemplate.send(..) part of code of my service class using unit testing?
public int invokeCallbackListener(final MyObject payload, final MyTask task) throws Exception{
//create map of payload and taskId
int taskStatusCd = task.getTaskSatus().getCode();
final Map<String, Object> map = new HashMap<String, Object>();
map.put(PAYLOAD_KEY, payload);
map.put(TASK_ID_KEY, task.getTaskId());
//generate JMSCorrelationID
final String correlationId = UUID.randomUUID().toString();
String requestQueue = System.getProperty("REQUEST_QUEUE");
requestQueue = requestQueue!=null?requestQueue:ExportConstants.DEFAULT_REQUEST_QUEUE;
jmsTemplate.send(requestQueue, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
***ObjectMessage message = session.createObjectMessage((Serializable)map)***; //fail here. Message returns null
message.setJMSCorrelationID(correlationId);
message.setStringProperty(MESSAGE_TYPE_PROPERTY,payload.getMessageType().getMessageType());
return message;
}
});
l.info("Request Message sent with correlationID: " + correlationId);
taskStatusCd = waitForResponseStatus(task.TaskId(), taskStatusCd, correlationId);
return taskStatusCd;
}
This is my test class code.
RemoteInvocationService remoteInvocationService;
JmsTemplate mockTemplate;
Session mockSession;
Queue mockQueue;
ObjectMessage mockMessage;
MessageCreator mockmessageCreator;
#Before
public void setUp() throws Exception {
remoteInvocationService = new RemoteInvocationService();
mockTemplate = mock(JmsTemplate.class);
mockSession = mock(Session.class);
mockQueue = mock(Queue.class);
mockMessage = mock(ObjectMessage.class);
mockmessageCreator = mock(MessageCreator.class);
when(mockSession.createObjectMessage()).thenReturn(mockMessage);
when(mockQueue.toString()).thenReturn("testQueue");
Mockito.doAnswer(new Answer<Message>() {
#Override
public Message answer(final InvocationOnMock invocation) throws JMSException {
final Object[] args = invocation.getArguments();
final String arg2 = (String)args[0];
final MessageCreator arg = (MessageCreator)args[1];
return arg.createMessage(mockSession);
}
}).when(mockTemplate).send(Mockito.any(MessageCreator.class));
mockTemplate.setDefaultDestination(mockQueue);
remoteInvocationService.setJmsTemplate(mockTemplate);
}
#Test
public void testMessage() throws Exception{
MyTask task = new MyTask();
task.setTaskSatus(Status.Pending);
remoteInvocationService.invokeCallbackListener(new MyObject(), task);
}
I have below code which receives message but, I am getting status object null.
Message receivedMsg = jmsTemplate.receiveSelected(responseQueue, messageSelector);if(receivedMsg instanceof TextMessage){
TextMessage status = (TextMessage) receivedMsg;
l.info(status.getText());}
below test code:
TextMessage mockTextMessage;
when(mockSession.createTextMessage()).thenReturn(mockTextMessage);
mockTextMessage.setText("5");
when(mockTemplate.receiveSelected(Mockito.any(String.class), Mockito.any(String.class))).thenReturn(mockTextMessage)
You are mocking the send method that accepts only one parameter (MessageCreator), but you are actually calling the one that accepts two (String, MessageCreator).
Add the String to your mock:
Mockito.doAnswer(new Answer<Message>() {
#Override
public Message answer(final InvocationOnMock invocation) throws JMSException {
final Object[] args = invocation.getArguments();
final MessageCreator arg = (MessageCreator)args[0];
return arg.createMessage(mockSession);
}
}).when(mockTemplate).send(Mockito.any(String.class), Mockito.any(MessageCreator.class));
There is another mistake when mocking the sesssion. You are mocking the method without parameterers:
when(mockSession.createObjectMessage()).thenReturn(mockMessage);
but you actually need to mock the one with the Serializable param:
when(mockSession.createObjectMessage(Mockito.any(Serializable.class)).thenReturn(mockMessage);

spring boot file uploading executes twice when MultipartException occurs

I want to upload files using spring-boot, and I have configured the properties right, and I also ensure the controller is correct, but the strange thing is the controller executed twice when I tried to upload a file larger exceed the limitation, what I expect is an error json message, and what I got is no response under the Postman.
Here is my controller,
#RestController
public class FileUploadController implements HandlerExceptionResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);
private static final String UPLOAD_PATH = "upload";
#ResponseBody
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = "multipart/form-data", produces = "application/json;charset=UTF-8")
public String upload(final MultipartFile file) {
try {
final Result<String> result = new Result<>();
if (file.isEmpty()) {
result.setSuccess(false);
result.setMessage("file is empty");
return Constants.OBJECT_MAPPER.writeValueAsString(result);
}
final File outputFile = new File(UPLOAD_PATH, UUID.randomUUID().toString());
FileUtils.writeByteArrayToFile(outputFile, file.getBytes());
result.setSuccess(true);
result.setMessage(outputFile.toString());
return Constants.OBJECT_MAPPER.writeValueAsString(result);
} catch (final Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ExceptionResultBuilder.build(ex);
}
}
#Override
public ModelAndView resolveException(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {
final ModelAndView modelAndView = new ModelAndView();
modelAndView.setView(new MappingJackson2JsonView());
final Map<String, Object> map = new HashMap<>();
map.put("success", false);
if (ex instanceof MultipartException) {
// if (LOGGER.isDebugEnabled()) {
LOGGER.info(ex.getMessage(), ex);
// }
final Throwable rootCause = ((MultipartException) ex).getRootCause();
if (rootCause instanceof SizeLimitExceededException) {
map.put("message", "request too large");
} else if (rootCause instanceof FileSizeLimitExceededException) {
map.put("message", "file too large");
} else {
map.put("message", "其他异常: " + rootCause.getMessage());
}
} else {
LOGGER.error(ex.getMessage(), ex);
}
modelAndView.addAllObjects(map);
return modelAndView;
}
}
and this is my property snippet for file uploading,
# MULTIPART (MultipartProperties)
multipart.enabled=true
multipart.max-file-size=5Mb
multipart.max-request-size=10Mb
If I tried to upload a file a bit larger than 5M, I will get the result like below under Postman, (the file size is 5208k)
enter image description here
and if I tried to upload a file between 5M and 10M, I will get this error, (the file size is 9748k)
enter image description here
I debugged into the controller and found that the resolveException method executed twice in a single upload.
Does anybody give me some tip?
The latest code list here, and I still got the same result,
#RestController
#ControllerAdvice
public class FileUploadController {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);
private static final String UPLOAD_PATH = "upload";
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = "multipart/form-data", produces = "application/json;charset=UTF-8")
public HttpEntity<?> upload(final MultipartFile file) {
try {
final Result<String> result = new Result<>();
if (file == null || file.isEmpty()) {
result.setSuccess(false);
result.setMessage("上传的文件为空");
return new ResponseEntity<Result<?>>(result, HttpStatus.OK);
}
final File outputFile = new File(UPLOAD_PATH, UUID.randomUUID().toString());
FileUtils.writeByteArrayToFile(outputFile, file.getBytes());
result.setSuccess(true);
result.setMessage(outputFile.toString());
return new ResponseEntity<Result<?>>(result, HttpStatus.OK);
} catch (final Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ExceptionResultBuilder.build(ex);
}
}
#ExceptionHandler(MultipartException.class)
public HttpEntity<?> multipartExceptionHandler(final MultipartException exception) {
LOGGER.error(exception.getMessage(), exception);
try {
final Result<String> result = new Result<>();
result.setSuccess(false);
final Throwable rootCause = ((MultipartException) exception).getRootCause();
if (rootCause instanceof SizeLimitExceededException) {
result.setMessage("请求过大");
} else if (rootCause instanceof FileSizeLimitExceededException) {
result.setMessage("文件过大");
} else {
result.setMessage("未知错误");
}
return new ResponseEntity<Result<?>>(result, HttpStatus.OK);
} catch (final Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ExceptionResultBuilder.build(ex);
}
}
}
I just go the same error and fix it by add the flowing code to my controller, good luck
#ExceptionHandler({ MultipartException.class, FileSizeLimitExceededException.class,
SizeLimitExceededException.class })
public ResponseEntity<Attachment> handleUploadrException(HttpServletRequest request, Throwable ex) {
Attachment result = new Attachment();
result.setDescription(ex.getMessage());
HttpStatus status = getStatus(request);
return new ResponseEntity<Attachment>(result, status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}

J2ME connect localhost nullpointerexception 0

I am trying to connect localhost and insert data into database through j2me application.but when I am connecting the server it shows there is a nullpointerexception 0 error.
this is midlet code
import java.io.DataOutputStream;
import java.io.InputStream;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.StringItem;
import javax.microedition.midlet.*;
public class Midlet_1 extends MIDlet implements CommandListener {
Display mdDisplay;
Form mForm;
StringItem messageitem;
Command exit, connectCommand;
public Midlet_1() {
mForm = new Form("My Counter midlet");
messageitem = new StringItem(null, "");
exit = new Command("Exit", Command.EXIT, 0);
connectCommand = new Command("Connect", Command.SCREEN, 0);
mForm.append(messageitem);
mForm.addCommand(exit);
mForm.addCommand(connectCommand);
mForm.setCommandListener(this);
}
public void startApp() {
mdDisplay = Display.getDisplay(this);
mdDisplay.setCurrent(mForm);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable d) {
if (c == exit) {
notifyDestroyed();
} else if (c == connectCommand) {
Form waitform = new Form("Waiting");
mdDisplay.setCurrent(waitform);
Thread t = new Thread() {
public void run() {
connect();
}
};
t.start();
}
}
private void connect() {
try {
HttpConnection hs = null;
InputStream in = null;
String url = "localhost:8080/testweb/src/save";
hs.setRequestProperty("User-Agent", "Profile/MIDP-2.0,Configuration/CLDC-2.0");
hs.setRequestProperty("Content-Language", "en-US");
hs.setRequestMethod(HttpConnection.POST);
DataOutputStream ds = hs.openDataOutputStream();
ds.writeUTF("nam56");
ds.writeUTF("67");
ds.writeUTF("0716522549");
ds.flush();
ds.close();
in = hs.openInputStream();
int connectlength = (int) hs.getLength();
byte[] raw = new byte[connectlength];
int length = in.read(raw);
// int ch;
// StringBuffer sb=new StringBuffer();
// while((ch=in.read())!=-1){
// sb.append((char)ch);
// }
in.close();
hs.close();
String s = new String(raw, 0, length);
messageitem.setText(s);
} catch (Exception e) {
messageitem.setText(e.toString());
System.out.println(e);
}
mdDisplay.setCurrent(mForm);
}
}
and this is servlet code
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, ClassNotFoundException, SQLException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
DataInputStream in=new DataInputStream(request.getInputStream());
String name=in.readUTF();
String id=in.readUTF();
String contt=in.readUTF();
Connection c=DBcon.setconConnection();
Statement s=c.createStatement();
s.executeUpdate("insert into details values('"+id+"','"+name+"''"+contt+"')");
out.print("successfullllll");
} finally {
out.close();
}
}
please check this out.....
This might work only if you are running an emulator on the same machine as the server. Try to replace locahost by 127.0.0.1.
In your connect() method, I can see that you initialized hs as null then you called setRequestProperty. Try to initialize hs properly before calling its methods.