Websocket acting different on different client languages - flutter

I have this Django Python Channels setup:
class MarketplaceConsumer(GenericAsyncAPIConsumer):
queryset = TestChannel.objects.all()
serializer_class = TestChannelSerializer
permission_classes = [permissions.AllowAny]
#model_observer(TestChannel)
async def comment_activity(self, message: TestChannelSerializer, observer=None, **kwargs):
await self.send_json(message.data)
#comment_activity.serializer
def comment_activity(self, instance: TestChannel, action, **kwargs) -> TestChannelSerializer:
'''This will return the comment serializer'''
return TestChannelSerializer(instance)
#action()
async def subscribe_to_comment_activity(self, user_pk, **kwargs):
print(user_pk) # PRINTS USER_PK PASSED THROUGH ACTION
await self.comment_activity.subscribe()
When this is run through JavaScript, I do get the expected result, the action prints the user_pk.
However, connecting to the same WebSocket using flutter, the user_pk is not printed, meaning the action specified is not even being called.
Am I implementing this incorrectly with the flutter client?
final channel = IOWebSocketChannel.connect(
Uri.parse('ws://127.0.0.1:8000/ws/marketplace/'));
#override
void initState() {
final data = json.encode({
'action': "subscribe_to_comment_activity",
'request_id': Helpers.generateId(),
'user_pk': 'PCs4o8c17xg',
});
channel.stream.listen((message) {
channel.sink.add(data);
//channel.sink.close(status.goingAway);
print(jsonDecode(message));
});
super.initState();
}

Figured this out! The flutter part somehow needs acknowledgement from the backend server. At least that's what I understand.
Adding an accept method to the backend works this out.
async def accept(self, **kwargs):
await super().accept(** kwargs)
print('connected')
await self.comment_activity.subscribe()

Related

How can I test this async piece of code in Flutter using Mockito?

I am trying to write some test for an app, but I can't test values stored in a provider. I am relatively new to testing, so there might be something that I am doing wrong, but anyway. What I want to test is to verify is two values are not the same, which should be the expected behavior, but I just can't make it pass.
This is the code that I want to test:
class RSAKeysProvider {
KeyPair? _keyPair;
KeyPair? get keyPair => _keyPair;
set setKeyPair(KeyPair keyPair) => _keyPair = keyPair;
Future<void> generate(String bits) async {
var keyPair = await RSA.generate(int.parse(bits));
_keyPair = keyPair;
notifyListeners();
}
}
I need to first call the generate() function, which will set the keyPair to actual values, and then check if keyPair.publicKey is different than keyPair.privateKey, but it gives me an error when I try to call generate() with await inside a test.
This is what I have for now, but it doesn't work. The test breaks when it cames to the line "await rsaKeys.generate('2048'). What can I do to make it work? I know the condition is not checking if both are different, but it is just a placeholder, I can't make the code arrive there!
test('Public and private key should be different', () async {
final MockRSAKeysProvider rsaKeys = MockRSAKeysProvider();
when(rsaKeys.generate(any)).thenAnswer((value) async {
KeyPair keyPair = await RSA.generate(2048);
rsaKeys.setKeyPair = keyPair;
});
await rsaKeys.generate('2048');
expect(rsaKeys.keyPair?.privateKey, isNotNull);
expect(rsaKeys.keyPair?.publicKey, isNotNull);
});
When it arrives at "await rsaKeys.generate('2048'), it gives me this error:
Invalid argument(s): Failed to load dynamic library 'librsa_bridge.dylib': dlopen(librsa_bridge.dylib, 0x0001): tried: 'librsa_bridge.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibrsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/./librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/../../../librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/Frameworks/librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/./librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/../../../librsa_bridge.dylib' (no such file), '/opt/homebrew/Caskroom/flutter/3.7.3/flutter/bin/cache/artifacts/engine/darwin-x64/Frameworks/librsa_bridge.dylib' (no such file), '/usr/lib/librsa_bridge.dylib' (no such file, not in dyld cache), 'librsa_bridge.dylib' (no such file), '/usr/lib/librsa_bridge.dylib' (no such file, not in dyld cache)
dart:ffi new DynamicLibrary.open
package:fast_rsa/bridge/binding.dart 117:33 Binding.openLib
package:fast_rsa/bridge/binding.dart 26:16 new Binding._internal
package:fast_rsa/bridge/binding.dart 17:45 Binding._singleton
package:fast_rsa/bridge/binding.dart Binding._singleton
package:fast_rsa/bridge/binding.dart 22:12 new Binding
package:fast_rsa/fast_rsa.dart 40:32 RSA.bindingEnabled
package:fast_rsa/fast_rsa.dart RSA.bindingEnabled
package:fast_rsa/fast_rsa.dart 43:9 RSA._call
package:fast_rsa/fast_rsa.dart 79:22 RSA._keyPairResponse
package:fast_rsa/fast_rsa.dart 437:18 RSA.generate
test/rsa_keys.test.dart 32:37 main.<fn>.<fn>.<fn>
package:mockito/src/mock.dart 185:45 Mock.noSuchMethod
test/rsa_keys.test.mocks.dart 75:53 MockRSAKeysProvider.generate
test/rsa_keys.test.dart 36:21 main.<fn>.<fn>
Your stub calls RSA.generate, so it depends on the RSA.generate implementation which involves FFI and loading a dynamically-loaded library on whatever platform you run tests on. Unless you're trying to test package:fastrsa itself, you should avoid calling things such as RSA.generate anyway; that is what you should be replacing with a stub.
The point of a Mock is to test how other code interacts with the mocked object. You cannot use a MockRSAKeysProvider to test the behavior of an RSAKeysProvider itself. If you want to test the behavior of your RSAKeysProvider class, you could change it to accept a stub for RSA.generate:
class RSAKeysProvider {
KeyPair? _keyPair;
KeyPair? get keyPair => _keyPair;
set setKeyPair(KeyPair keyPair) => _keyPair = keyPair;
Future<void> generate(String bits, {Future<KeyPair> Function(int)? generate}) async {
generate ??= RSA.generate;
var keyPair = await generate(int.parse(bits));
_keyPair = keyPair;
notifyListeners();
}
}
and then in your test:
Future<KeyPair> fakeGenerate(int bits) async {
return KeyPair('fakePublicKey', 'fakePrivateKey'); // Or use precomputed values.
}
test('Public and private key should not be null when generated', () async {
final RSAKeysProvider rsaKeys = RSAKeysProvider();
await rsaKeys.generate('2048', generate: fakeGenerate);
expect(rsaKeys.keyPair?.privateKey, isNotNull);
expect(rsaKeys.keyPair?.publicKey, isNotNull);
});

How display data which comes from async function?

I use api for get information which need to be display
Future <String> Get_Amount_Jackpot() async {
// SERVER LOGIN API URL
var url2 = 'https://www.easytrafic.fr/game_app/get_jackpot_lotto.php';
// Starting Web API Call.
var response2 = await http.get(url2,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
// Getting Server response into variable.
Map<String, dynamic> jsondata2 = json.decode(response2.body);
return jsondata2["value"];
}
I call it here :
void initState() {
ListLotto = Grille_display();
jackpot = Get_Amount_Jackpot();
super.initState();;
}
How i can display the value "jackpot" which is a simple number on mobile screen. Note i use futurebuilder for another api request, this is why it is complicated. Normally i use futurebuilder for display async data but there i need 2 différents api request so 2 futureBuilder ??? Is it possible and how do that ?
You can use then to get the returned value from the function.
Get_Amount_Jackpot().then((value){
//value is what is returned from the function
jackpot = value;
});
I'm not sure if you can use uppercase letter as the start of a function name, but i copied your function name for my answer. Hope this helps.

Futures in Dart / flutter_sound examples not working

Start tinkering with Dart/Flutter, I'm trying to record and play audio. Examples provided in this library: https://github.com/dooboolab/flutter_sound show async code in Dart using Futures.
Future<String> result = await flutterSound.startRecorder(null);
result.then(path) {
print('startRecorder: $path');
_recorderSubscription = flutterSound.onRecorderStateChanged.listen((e) {
DateTime date = new DateTime.fromMillisecondsSinceEpoch(e.currentPosition.toInt());
String txt = DateFormat('mm:ss:SS', 'en_US').format(date);
});
}
However this code doesn't even compile at my system so Im wondering what I'm missing. In order to compile this code I have to change it to something like:
Future<String> result = widget._flutterSound.startRecorder(null);
result.then((path) {
print('startRecorder: $path');
var _recorderSubscription = widget._flutterSound.onRecorderStateChanged.listen((e) {
DateTime date = new DateTime.fromMillisecondsSinceEpoch(e.currentPosition.toInt());
print(date);
});
});
What am I missing?
Have you try this:
Future<String> result() async => flutterSound.startRecorder(null);
when you working with futures: async and await: https://dart.dev/codelabs/async-await
The async and await keywords provide a declarative way to define asynchronous functions and use their results. Remember these two basic guidelines when using async and await:
To define an async function, add async before the function body.
The await keyword works only in async functions.

How to test async functions that update PublishSubject or BehaviorSubject object (RxDart) in Flutter

I've been learning flutter for a few weeks and come from an Android background so far I love it and I have also been delighted to find that Flutter was designed with testing in mind from day one. However, I've been having an issue running the following test.
main() => {
test('test get popular repos', () async {
final testOwner = Owner(1010, "testLink");
final testRepo =
Repo(101, testOwner, "testRepo", "description", 'htmlUrl', 500);
final testRepoResponse = RepoResponse(List.from([testRepo]), null);
final uiModel = PopRepo(testRepo.owner.avatarUrl, testRepo.name,
testRepo.description, "Stars: ${testRepo.stargazersCount}");
final searchData = SearchData(List.from([uiModel]), null);
final Repository mockRepository = _mockRepository();
when(mockRepository.getPopularReposForOrg("org"))
.thenAnswer((_) => Future.value(testRepoResponse));
final repoSearchBloc = RepoSearchPageBloc(mockRepository);
await repoSearchBloc.getPopularRepos("org");
await expectLater(repoSearchBloc.resultSubject.stream, emits(searchData));
}),
};
class _mockRepository extends Mock implements Repository {}
My RepoSearchBloc takes data from a Repository and transforms it into the Ui model. Finally it posts that now UI-ready data to the Subject
this is the method under test in the RepoSearchBloc
getPopularRepos(String org) async {
if (org == null || org.isEmpty)
return resultSubject.add(SearchData(List(), null));
RepoResponse response = await _repository.getPopularReposForOrg(org);
if (response.error == null) {
List<Repo> repoList = response.results;
repoList.sort((a, b) => a.stargazersCount.compareTo(b.stargazersCount));
var uiRepoList = repoList
.map((repo) => PopRepo(repo.owner.avatarUrl, repo.name,
repo.description, "Stars: ${repo.stargazersCount}"))
.take(3)
.toList();
resultSubject.add(SearchData(uiRepoList, null));
} else {
ErrorState error = ErrorState(response.error);
resultSubject.add(SearchData(List(), error));
}
When I run the test I keep getting this message no matter what I do it seems with either BehaviorSubject or PublishSubject:
ERROR: Expected: should emit an event that <Instance of 'SearchData'>
Actual: <Instance of 'BehaviorSubject<SearchData>'>
Which: emitted * Instance of 'SearchData'
Any ideas how to get this test to pass?
Ended up figuring this out with the help of a user Nico #Rodsevich of the Flutter Glitter community
anyways using his suggestion to use await for
I came up with the following solution which passed
await for (var emittedResult in repoSearchBloc.resultSubject.stream) {
expect(emittedResult.results[0].repoName, testRepo.name);
return;
}
The RxDart library has some subject tests for reference but my subject being posted to asynchronously did not adhere to their test cases so this solution ended up being just what I needed.
Also #Abion47 's comment also seems to do the job when I move async inside the parameter for expected
expectLater( (await repoSearchBloc.resultSubject.stream.first as SearchData).results[0].repoName, testRepo.name);

Trouble with testing using MockClient in Flutter

I am trying to write a simple test in flutter using MockClient, but I can't seem to get it to work.
Here is the code I am trying to test:
getItemById(int id) async {
final response = await client.get("$_host/item/$id.json");
final decodedJson = json.decode(response.body);
return Item.fromJson(decodedJson);
}
Here is the test code:
test("Test getting item by id", () async {
final newsApi = NewsAPI();
newsApi.client = MockClient((request) async {
final jsonMap = {'id': 123};
Response(json.encode(jsonMap), 200);
});
final item = await newsApi.getItemById(123);
print("Items: ${item.toString()}"); //<-- dosen't print anything.
expect(item.id , 123);
});
When I run the test, it fails with the following message:
NoSuchMethodError: The getter 'bodyBytes' was called on null.
Receiver: null
Tried calling: bodyBytes
I am guessing the issue here is that nothing is returned from the MockClient when I make the call to the getItemById method, but I am not sure why.
I had the same exact issue. You have to return the Response
return Response(json.encode(jsonMap), 200);
Mock expects test function to be EXACTLY as you real function (including OPTIONAL parameters and so on). If both does not match it returns NULL and that is what is happening with your code. Double check to see where your test function is different of original function.