How to handle unexpected asynchronous exceptions with runZonedGuarded? - flutter

I follow examples in runZonedGuarded function to setup reporting of unhandled async exceptions.
It works, but every output in console now prints twice:
sso: started silent sign-in
sso: started silent sign-in
sso: no user stored
sso: no user stored
async error
async error
Why it is and how can I fix it?
The code:
runZonedGuarded(
() => runApp(MyApp(config)),
(error, stackTrace) => print('async error'),
);

The first parameter for runZonedGuarded should be async function, not sync.
I added word 'async' and if fixed the issue:
runZonedGuarded(
() async => runApp(MyApp(config)),
(error, stackTrace) => print('async error'),
);

Related

While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW)

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.
i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

Flutter Bloc Test verify/running a function after get result

I try to test bloc (AuthBloc) which running "logout" when getLocalToken is failed and I get this error.
How can I verify/running logout after I get result ServerException on my bloctest.
Please Help.
Thanks before.
My Bloc :
on<AppStarted>((event, emit) async {
final token = await getLocalToken(const Params(needValidation: true));
token.fold(
(failure) {
logout(NoParams());
emit(AuthNotAuthenticated());
},
(auth) => emit(AuthAuthenticated()),
);
});
Here My Test Code :
blocTest<AuthBloc, AuthState>(
'should emit [AuthNotAuthenticated] when token / input is invalid',
build: () {
when(mockGetLocalToken(any))
.thenAnswer((_) async => Left(ServerFailure()));
return bloc;
},
act: (blc) => blc.add(AppStarted()),
expect: () { return <AuthState>[AuthNotAuthenticated()];},
);
Error :
00:03 +2 -1: AppStarted should emit [AuthNotAuthenticated] when token / input is invalid [E]
MissingStubError: 'call'
No stub was found which matches the arguments of this method call:
call(NoParams())
Add a stub for this method using Mockito's 'when' API, or generate the MockLogout mock with a MockSpec with 'returnNullOnMissingStub: true'
Expected: [AuthNotAuthenticated:AuthNotAuthenticated()]
Actual: []
Which: at location [0] is [] which shorter than expected
==== diff ========================================
[[-AuthNotAuthenticated()-]]
==== end diff ====================================

Update a widget in mobile app on a new database record

I am developing an app using flutter.
The backend is in flask and the database is a nosql (mongodb).
I have a widget which shows users who have liked an article.
What I want to achieve is:
A) if user X likes an article that I am currently viewing I want to instantly see that (ie his avatar, name, doesn’t matter).
B) let’s say I am on another screen (ie settings), I want the same to happen in the background so when I go back to the article screen, to see the user that has liked the article (if that’s possible).
What I know so far is that I have to use a stream builder (flutter). This is fine, I understand how that works.
My question is how am I going to push the new server event/database record(user X likes article Y) to the app.
Should I use server sent events, websockets or there is another solution?
If sse and ws are the only solutions which one is preferable given that I’m using flask as my backend?
Almost all of the examples I have seen online are unfortunately using firebase (not all of us want to use firebase!) and none of the websocket examples mention anything about data we want to retrieve from a db.
Any reference to a helpful tutorial (no firebase!) would be very helpful.
I'm doing something similar for a chat app, i had to use flask-socketio to emit events from flask and socket_io_client: ^2.0.0-beta.4-nullsafety.0 on flutter along with flutter_bloc to handle my state. I'm using the beta flutter socket_io_client because my server is using socketio 4 which is not supported by the current stable version.
Flutter
#injectable
class SocketBloc extends Bloc<SocketEvent, SocketState> {
final Socket _socket;
SocketBloc(this._socket)
: super(SocketState.initial()) {
on<SocketEvent>((event, emit) async {
await event.map(
connecting: (value) async => await _mapConnectingToState(emit, value),
connected: (value) async => await _mapConnectToState(emit, value),
disconnected: (value) async => await _mapDisconnectToState(emit, value),
error: (value) async => await _mapErrorToState(emit, value),
connect: (value) async => await _mapSocketConnectToState(emit, value),
inquiries: (value) async => await _mapAddInquiriesToState(emit, value),
setActiveInquiry: (value) async =>
await _mapSetActiveInquiryToState(emit, value),
newChatMessage: (value) async =>
await _mapNewChatMessageToState(emit, value),
newMessage: (value) async => await _mapNewMessageToState(emit, value),
sendChat: (value) async => await _mapSendChatToState(emit, value),
uploadFiles: (value) async => await _mapUploadFilesToState(emit, value),
);
});
_setUpListeners();
}
void _setUpListeners() {
_socket.onConnecting((data) => add(const SocketEvent.connecting()));
_socket.onConnect((data) => add(const SocketEvent.connected()));
_socket.onConnectError((data) => add(const SocketEvent.error()));
_socket.onDisconnect((data) => add(const SocketEvent.disconnected()));
_socket.on(
Events.INQUIRIES.name,
(data) => add(SocketEvent.inquiries(data)),
);
_socket.on(
Events.NEW_CHAT.name,
(data) => add(SocketEvent.newChatMessage(data)),
);
_socket.on(
Events.NEW_MESSAGE.name,
(data) => add(SocketEvent.newMessage(data)),
);
}
Flask
#socketio.on(prefix)
def handle_new_message(data):
admin = get_admin()
if not admin:
raise Exception("Admin not configured")
# Get user by sid
db_user = User.objects.get(session=request.sid)
if not db_user:
raise Exception("No such client")
message = data.get('message')
if not message:
raise Exception("No message content")
message_id = data.get('message_id')
if not message_id:
raise Exception("No message ID")
db_message = Message.objects.get(pk=message_id)
if not db_message:
raise Exception("No such message")
# TODO - Affirm that sender is owner of message
chat = Chat(message=message,
message_id=message_id,
recipient=admin.uid,
sender=db_user.uid)
chat.save()
emit(SocketEvents.NEW_CHAT.name, JSONEncoder().encode(clear_cls(chat.to_mongo().to_dict())))
db_message.last_message = chat.message
db_message.save()
emit(SocketEvents.NEW_MESSAGE.name, JSONEncoder().encode(clear_cls(db_message.to_mongo().to_dict())))

What is the proper way of detecting network errors in stream downloads using Axios in Nest.js?

I'm working with the HttpService module from Nest.js to make the HTTP calls. I'm able to download an image from https://unsplash.com; when there is no network interruptions the code is working as expected.
This is the code I have for making the download call and start writing into the desired file
const urlDownload = 'https://unsplash.com/photos/xiie4XeSzTU/download?force=true';
let response = await this.httpService.get(urlDownload, {
responseType: 'stream'
}).toPromise();
response.data.pipe(writer);
And this is the code where I'm trying to handle the possible events of the writer and returning a response
let downloadFile = path.resolve(__dirname,'../../files/landscape.jpg');
let writer = fs.createWriteStream(downloadFile);
return new Promise((resolve, reject) => {
writer.on('finish', ()=>{
resolve('Image downloaded');
});
writer.on('error', ()=>{
reject('Image downloaded failed');
});
});
I'm deliberately turning off the wifi during the download to try the server response with Image downloaded failed (what I have in the writer error handler), but instead I'm getting an 500 statusCode, internal server error. When I go to the Nest console to whatch the error it appears
[Nest] 11220 - 2020-05-22 18:16:45 [ExceptionsHandler] getaddrinfo ENOTFOUND unsplash.com +439536ms
Error: getaddrinfo ENOTFOUND unsplash.com
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:64:26)
How can I solve this and catch correcty the network error from Nest to return a friendly message?
I could solve it. I let it here with the hope of helping somebody in the future.
It is not firing the error handler function because that handler is attached to the writter, and there is not writting error, it just stops writing because the cut of the connection but that is not an error.
I re-writed the response variable to stop being a promise and better I started treating it like an observer.
let response = this.httpService.get(urlDownload, {
responseType: 'stream',
});
And then it is the response in previus Promise format
return new Promise((resolve, reject) => {
writer.on('error', () => {
resolve('error due to, possibly, an unexisting file path');
})
response.subscribe({
next(response) { response.data.pipe(writer) },
error(err) {
console.error('More details: ' + err);
resolve('Error in the download :/')
},
complete() { resolve('Completed'); }
})
});
I'm not using the reject function of the promise but it is perfectly doable

Loopback 4: Test never end when there is a connection with a MongoDB

Launch npm test, but it never end. It happens when there is a connection with a MongoDB involve. It is rare because the test works, but it never ends.
before('setupApplication', async () => {
({ app, client } = await setupApplication());
//await prepareTestData()
});
after(async () => {
//await cleanupTestData();
await app.stop();
});
it('login with a test user', async () => {
const res = await client.post('/user-accounts/login').set('urlLogin', TEST_TENANT_URL)
.send({
email: TEST_EMAIL,
password: TEST_PASS
}).expect(200);
token = res.body.token;
expect(res.body).to.ownProperty("token").and.length(EXPECTED_TOKEN_LENGTH);
console.log("Logged in with token ", res.body.token);
});
The problem: The terminal never end. Is like a thread still working. I don´t know if it is a problem with loopback or what.
Any idea? I hope somebody could help me.
Thanks.
Cross-posting my comment from the discussion in https://github.com/strongloop/loopback-next/issues/3672:
Please use Mocha option exit, it will end the process after all tests (and after/afterEach hooks) have finished. See https://mochajs.org/#configuring-mocha-nodejs to learn more. Alternatively, you can add --exit option to mocha CLI, e.g. mocha --exit src/__tests__/**/*.js.