From b5863111372fc6e7ceaf5a6f91ac9a887a6a9466 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Fri, 27 Sep 2024 01:38:15 +0530 Subject: [PATCH 01/23] changed version of websocket --- packages/at_lookup/lib/at_lookup.dart | 2 + .../at_lookup/lib/src/at_lookup_impl.dart | 66 ++++++++++++++++++- .../lib/src/connection/at_connection.dart | 8 +-- .../lib/src/connection/base_connection.dart | 22 +++---- .../connection/base_socket_connection.dart | 60 +++++++++++++++++ .../src/connection/outbound_connection.dart | 4 +- .../connection/outbound_connection_impl.dart | 4 +- .../connection/outbound_message_listener.dart | 2 +- .../outbound_websocket_connection.dart | 11 ++++ .../outbound_websocket_connection_impl.dart | 33 ++++++++++ .../at_lookup/lib/src/monitor_client.dart | 2 +- .../lib/src/util/secure_socket_util.dart | 47 +++++++++---- packages/at_lookup/pubspec.yaml | 2 +- .../test/connection_management_test.dart | 32 ++++----- 14 files changed, 243 insertions(+), 52 deletions(-) create mode 100644 packages/at_lookup/lib/src/connection/base_socket_connection.dart create mode 100644 packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart create mode 100644 packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart diff --git a/packages/at_lookup/lib/at_lookup.dart b/packages/at_lookup/lib/at_lookup.dart index b013990c..e520c1a6 100644 --- a/packages/at_lookup/lib/at_lookup.dart +++ b/packages/at_lookup/lib/at_lookup.dart @@ -6,9 +6,11 @@ library at_lookup; export 'src/at_lookup.dart'; export 'src/at_lookup_impl.dart'; export 'src/connection/outbound_connection.dart'; +export 'src/connection/outbound_websocket_connection.dart'; export 'src/connection/outbound_connection_impl.dart'; export 'src/exception/at_lookup_exception.dart'; export 'src/monitor_client.dart'; export 'src/cache/secondary_address_finder.dart'; export 'src/cache/cacheable_secondary_address_finder.dart'; export 'src/util/secure_socket_util.dart'; +export 'src/connection/outbound_websocket_connection_impl.dart'; diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 550c1801..2ee18d95 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -25,6 +25,10 @@ class AtLookupImpl implements AtLookUp { OutboundConnection? get connection => _connection; + OutboundWebSocketConnection? _webSocketConnection; + + // OutboundWebSocketConnection? get _webSocketConnection => _webSocketConnection; + @override late SecondaryAddressFinder secondaryAddressFinder; @@ -238,7 +242,7 @@ class AtLookupImpl implements AtLookUp { return putResult.isNotEmpty; } - Future createConnection() async { + Future createConnection({bool useWebSocket = false}) async { if (!isConnectionAvailable()) { if (_connection != null) { // Clean up the connection before creating a new one @@ -261,6 +265,34 @@ class AtLookupImpl implements AtLookUp { } } + Future createWebSocketConnection() async { + if (!isConnectionAvailable()) { + if (_connection != null) { + // Clean up the connection before creating a new one + logger.finer('Closing old connection'); + await _connection!.close(); + } + logger.info('Creating new connection'); + + // 1. Find secondary URL for the atsign from the lookup library + SecondaryAddress secondaryAddress = + await secondaryAddressFinder.findSecondary(_currentAtSign); + var host = secondaryAddress.host; + var port = secondaryAddress.port; + + // Create WebSocket connection + logger.info('Using WebSocket connection'); + await createOutBoundWebSocketConnection(host, port.toString(), _currentAtSign, _secureSocketConfig); + + messageListener = socketListenerFactory + .createWebSocketListener(_connection as OutboundWebSocketConnection); + messageListener.listen(); + logger.info('New connection created OK'); + } + } + + + /// Executes the command returned by [VerbBuilder] build command on a remote secondary server. /// Catches any exception and throws [AtLookUpException] @override @@ -645,6 +677,23 @@ class AtLookupImpl implements AtLookUp { return true; } + Future createOutBoundWebSocketConnection(String host, String port, + String toAtSign, SecureSocketConfig secureSocketConfig) async { + try { + WebSocket secureSocket = + await socketFactory.createWebSocket(host, port, secureSocketConfig); + _webSocketConnection = + outboundConnectionFactory.createWebSocketOutboundConnection(secureSocket); + if (outboundConnectionTimeout != null) { + _connection!.setIdleTime(outboundConnectionTimeout); + } + } on SocketException { + throw SecondaryConnectException( + 'unable to connect to secondary $toAtSign on $host:$port'); + } + return true; + } + bool isConnectionAvailable() { return _connection != null && !_connection!.isInValid(); } @@ -688,6 +737,11 @@ class AtLookupSecureSocketFactory { String host, String port, SecureSocketConfig socketConfig) async { return await SecureSocketUtil.createSecureSocket(host, port, socketConfig); } + + Future createWebSocket( + String host, String port, SecureSocketConfig socketConfig) async { + return await SecureSocketUtil.createSecureSocket(host, port, socketConfig, isWebSocket: true); + } } class AtLookupSecureSocketListenerFactory { @@ -695,10 +749,20 @@ class AtLookupSecureSocketListenerFactory { OutboundConnection outboundConnection) { return OutboundMessageListener(outboundConnection); } + + OutboundMessageListener createWebSocketListener( + OutboundWebSocketConnection outboundWebSocketConnection) { + return OutboundMessageListener(outboundWebSocketConnection); + } } class AtLookupOutboundConnectionFactory { OutboundConnection createOutboundConnection(SecureSocket secureSocket) { return OutboundConnectionImpl(secureSocket); } + +// introduce new createWebSocketCOnnection + OutboundWebSocketConnection createWebSocketOutboundConnection(WebSocket webSocket) { + return OutboundWebsocketConnectionImpl(webSocket); + } } diff --git a/packages/at_lookup/lib/src/connection/at_connection.dart b/packages/at_lookup/lib/src/connection/at_connection.dart index dd2d8d84..739d4758 100644 --- a/packages/at_lookup/lib/src/connection/at_connection.dart +++ b/packages/at_lookup/lib/src/connection/at_connection.dart @@ -1,14 +1,12 @@ -import 'dart:io'; +abstract class AtConnection { + /// The underlying connection + T get underlying; -abstract class AtConnection { /// Write a data to the underlying socket of the connection /// @param - data - Data to write to the socket /// @throws [AtIOException] for any exception during the operation void write(String data); - /// Retrieves the socket of underlying connection - Socket getSocket(); - /// closes the underlying connection Future close(); diff --git a/packages/at_lookup/lib/src/connection/base_connection.dart b/packages/at_lookup/lib/src/connection/base_connection.dart index 0b6354a0..3263f83d 100644 --- a/packages/at_lookup/lib/src/connection/base_connection.dart +++ b/packages/at_lookup/lib/src/connection/base_connection.dart @@ -5,17 +5,17 @@ import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_utils/at_logger.dart'; /// Base class for common socket operations -abstract class BaseConnection extends AtConnection { +abstract class BaseConnection extends AtConnection { + final T _socket; late final AtSignLogger logger; - late final Socket _socket; + // late final Socket _socket; StringBuffer? buffer; AtConnectionMetaData? metaData; - BaseConnection(Socket? socket) { + BaseConnection(this._socket) { logger = AtSignLogger(runtimeType.toString()); buffer = StringBuffer(); - socket?.setOption(SocketOption.tcpNoDelay, true); - _socket = socket!; + _socket.setOption(SocketOption.tcpNoDelay, true); } @override @@ -31,8 +31,8 @@ abstract class BaseConnection extends AtConnection { } try { - var address = _socket.remoteAddress; - var port = _socket.remotePort; + var address = underlying.remoteAddress; + var port = underlying.remotePort; logger.info('close(): calling socket.destroy()' ' on connection to $address:$port'); @@ -46,10 +46,8 @@ abstract class BaseConnection extends AtConnection { } } - @override - Socket getSocket() { - return _socket; - } + @override + T get underlying => _socket; @override Future write(String data) async { @@ -58,7 +56,7 @@ abstract class BaseConnection extends AtConnection { throw ConnectionInvalidException('write(): Connection is invalid'); } try { - getSocket().write(data); + underlying.write(data); getMetaData()!.lastAccessed = DateTime.now().toUtc(); } on Exception { getMetaData()!.isStale = true; diff --git a/packages/at_lookup/lib/src/connection/base_socket_connection.dart b/packages/at_lookup/lib/src/connection/base_socket_connection.dart new file mode 100644 index 00000000..d4695330 --- /dev/null +++ b/packages/at_lookup/lib/src/connection/base_socket_connection.dart @@ -0,0 +1,60 @@ +import 'dart:io'; +import 'package:at_commons/at_commons.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; +import 'package:at_utils/at_logger.dart'; + +/// WebSocket-specific connection class +abstract class BaseSocketConnection extends AtConnection { + final WebSocket _webSocket; + late final AtSignLogger logger; + StringBuffer? buffer; + AtConnectionMetaData? metaData; + + BaseSocketConnection(this._webSocket) { + logger = AtSignLogger(runtimeType.toString()); + buffer = StringBuffer(); + } + + @override + AtConnectionMetaData? getMetaData() { + return metaData; + } + + @override + Future close() async { + if (getMetaData()!.isClosed) { + logger.finer('close(): WebSocket connection is already closed'); + return; + } + + try { + logger.info('close(): closing WebSocket connection'); + await _webSocket.close(); + } catch (e) { + // Ignore errors or exceptions on connection close + logger.finer('Exception "$e" while closing WebSocket - ignoring'); + getMetaData()!.isStale = true; + } finally { + getMetaData()!.isClosed = true; + } + } + + @override + WebSocket get underlying => _webSocket; + + @override + Future write(String data) async { + if (isInValid()) { + throw ConnectionInvalidException( + 'write(): WebSocket connection is invalid'); + } + + try { + _webSocket.add(data); // WebSocket uses add() to send data + getMetaData()!.lastAccessed = DateTime.now().toUtc(); + } on Exception { + getMetaData()!.isStale = true; + } + } + +} diff --git a/packages/at_lookup/lib/src/connection/outbound_connection.dart b/packages/at_lookup/lib/src/connection/outbound_connection.dart index 75a290d7..3581a816 100644 --- a/packages/at_lookup/lib/src/connection/outbound_connection.dart +++ b/packages/at_lookup/lib/src/connection/outbound_connection.dart @@ -2,8 +2,8 @@ import 'dart:io'; import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_lookup/src/connection/base_connection.dart'; -abstract class OutboundConnection extends BaseConnection { - OutboundConnection(Socket socket) : super(socket); +abstract class OutboundConnection extends BaseConnection { + OutboundConnection(T super.socket); void setIdleTime(int? idleTimeMillis); } diff --git a/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart b/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart index 4a79e58c..45f3666b 100644 --- a/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart +++ b/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'outbound_connection.dart'; -class OutboundConnectionImpl extends OutboundConnection { +class OutboundConnectionImpl extends OutboundConnection { int? outboundIdleTime = 600000; //default timeout 10 minutes - OutboundConnectionImpl(Socket socket) : super(socket) { + OutboundConnectionImpl(T socket) : super(socket) { metaData = OutboundConnectionMetadata()..created = DateTime.now().toUtc(); } diff --git a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart b/packages/at_lookup/lib/src/connection/outbound_message_listener.dart index 81d9d91a..6d90386f 100644 --- a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart +++ b/packages/at_lookup/lib/src/connection/outbound_message_listener.dart @@ -30,7 +30,7 @@ class OutboundMessageListener { runZonedGuarded(() { _connection - .getSocket() + .underlying .listen(messageHandler, onDone: onSocketDone, onError: onSocketError); }, (Object error, StackTrace st) { logger.warning( diff --git a/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart b/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart new file mode 100644 index 00000000..da3ac185 --- /dev/null +++ b/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart @@ -0,0 +1,11 @@ +import 'dart:io'; +import 'package:at_lookup/src/connection/base_socket_connection.dart'; + + +abstract class OutboundWebSocketConnection extends BaseSocketConnection { + final WebSocket webSocket; + + OutboundWebSocketConnection(this.webSocket) : super(webSocket); + + void setIdleTime(int? idleTimeMillis); +} diff --git a/packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart b/packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart new file mode 100644 index 00000000..9f7279c1 --- /dev/null +++ b/packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart @@ -0,0 +1,33 @@ +import 'dart:io'; +import 'outbound_websocket_connection.dart'; +import 'outbound_connection.dart'; + +class OutboundWebsocketConnectionImpl + extends OutboundWebSocketConnection { + int? outboundIdleTime = 600000; //default timeout 10 minutes + + OutboundWebsocketConnectionImpl(T websocket) : super(websocket) { + metaData = OutboundConnectionMetadata()..created = DateTime.now().toUtc(); + } + + int _getIdleTimeMillis() { + var lastAccessedTime = getMetaData()!.lastAccessed; + lastAccessedTime ??= getMetaData()!.created; + var currentTime = DateTime.now().toUtc(); + return currentTime.difference(lastAccessedTime!).inMilliseconds; + } + + bool _isIdle() { + return _getIdleTimeMillis() > outboundIdleTime!; + } + + @override + bool isInValid() { + return _isIdle() || getMetaData()!.isClosed || getMetaData()!.isStale; + } + + @override + void setIdleTime(int? idleTimeMillis) { + outboundIdleTime = idleTimeMillis; + } +} diff --git a/packages/at_lookup/lib/src/monitor_client.dart b/packages/at_lookup/lib/src/monitor_client.dart index 49f3c3dd..1c14dea4 100644 --- a/packages/at_lookup/lib/src/monitor_client.dart +++ b/packages/at_lookup/lib/src/monitor_client.dart @@ -28,7 +28,7 @@ class MonitorClient { var _monitorConnection = await _createNewConnection(_atSign, _rootDomain, _rootPort); //2. Listener on _monitorConnection. - _monitorConnection.getSocket().listen((event) { + _monitorConnection.underlying.listen((event) { response = utf8.decode(event); // If response contains data to be notified, invoke callback function. if (response.toString().startsWith('notification')) { diff --git a/packages/at_lookup/lib/src/util/secure_socket_util.dart b/packages/at_lookup/lib/src/util/secure_socket_util.dart index 6eff0733..17e0972d 100644 --- a/packages/at_lookup/lib/src/util/secure_socket_util.dart +++ b/packages/at_lookup/lib/src/util/secure_socket_util.dart @@ -2,14 +2,38 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; class SecureSocketUtil { - ///method that creates and returns a [SecureSocket]. If [decryptPackets] is set to true,the TLS keys are logged into a file. - static Future createSecureSocket( + /// Method that creates and returns either a [SecureSocket] or a [WebSocket]. + /// If [decryptPackets] is set to true, the TLS keys are logged into a file. + static Future createSecureSocket( + String host, String port, SecureSocketConfig secureSocketConfig, + {bool isWebSocket = false}) async { + if (isWebSocket) { + return createSecureWebSocket(host, port, secureSocketConfig); + } else { + return _createSecureSocket(host, port, secureSocketConfig); + } + } + + /// Creates a secure WebSocket connection. + static Future createSecureWebSocket( + String host, String port, SecureSocketConfig secureSocketConfig) async { + try { + final uri = Uri.parse('wss://$host:$port'); + WebSocket webSocket = await WebSocket.connect(uri.toString()); + return webSocket; + } catch (e) { + throw AtException('Error creating WebSocket connection: ${e.toString()}'); + } + } + + /// Creates a secure socket connection (SecureSocket). + static Future _createSecureSocket( String host, String port, SecureSocketConfig secureSocketConfig) async { - SecureSocket? _secureSocket; + SecureSocket? secureSocket; if (!secureSocketConfig.decryptPackets) { - _secureSocket = await SecureSocket.connect(host, int.parse(port)); - _secureSocket.setOption(SocketOption.tcpNoDelay, true); - return _secureSocket; + secureSocket = await SecureSocket.connect(host, int.parse(port)); + secureSocket.setOption(SocketOption.tcpNoDelay, true); + return secureSocket; } else { SecurityContext securityContext = SecurityContext(); try { @@ -20,16 +44,17 @@ class SecureSocketUtil { .setTrustedCertificates(secureSocketConfig.pathToCerts!); } else { throw AtException( - 'decryptPackets set to true but path to trusted certificated not provided'); + 'decryptPackets set to true but path to trusted certificates not provided'); } - _secureSocket = await SecureSocket.connect(host, int.parse(port), + secureSocket = await SecureSocket.connect(host, int.parse(port), context: securityContext, keyLog: (line) => keysFile.writeAsStringSync(line, mode: FileMode.append)); - _secureSocket.setOption(SocketOption.tcpNoDelay, true); - return _secureSocket; + secureSocket.setOption(SocketOption.tcpNoDelay, true); + return secureSocket; } catch (e) { - throw AtException(e.toString()); + throw AtException( + 'Error creating SecureSocket connection: ${e.toString()}'); } } } diff --git a/packages/at_lookup/pubspec.yaml b/packages/at_lookup/pubspec.yaml index caf17e64..6487584c 100644 --- a/packages/at_lookup/pubspec.yaml +++ b/packages/at_lookup/pubspec.yaml @@ -6,7 +6,7 @@ homepage: https://atsign.com documentation: https://docs.atsign.com/ environment: - sdk: '>=2.15.0 <4.0.0' + sdk: '>=2.17.0 <4.0.0' dependencies: path: ^1.8.0 diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index 46de1a5e..1105aba1 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -61,7 +61,7 @@ void main() { // let's get a handle to the first socket & connection OutboundConnection firstConnection = atLookup.connection!; MockSecureSocket firstSocket = - firstConnection.getSocket() as MockSecureSocket; + firstConnection.underlying as MockSecureSocket; expect(firstSocket.mockNumber, 1); expect(firstSocket.destroyed, false); @@ -85,7 +85,7 @@ void main() { // has a new connection been created, with a new socket? OutboundConnection secondConnection = atLookup.connection!; MockSecureSocket secondSocket = - secondConnection.getSocket() as MockSecureSocket; + secondConnection.underlying as MockSecureSocket; expect(firstConnection.hashCode == secondConnection.hashCode, false); expect(secondSocket.mockNumber, 2); expect(secondSocket.destroyed, false); @@ -99,10 +99,10 @@ void main() { OutboundConnection oc = OutboundConnectionImpl( createMockAtServerSocket('test.test.test', 12345)); OutboundMessageListener oml = OutboundMessageListener(oc); - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); expect(oc.metaData?.isClosed, false); oml.onSocketDone(); - expect((oc.getSocket() as MockSecureSocket).destroyed, true); + expect((oc.underlying as MockSecureSocket).destroyed, true); expect(oc.metaData?.isClosed, true); }); @@ -112,10 +112,10 @@ void main() { OutboundConnection oc = OutboundConnectionImpl( createMockAtServerSocket('test.test.test', 12345)); OutboundMessageListener oml = OutboundMessageListener(oc); - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); expect(oc.metaData?.isClosed, false); oml.onSocketError('test'); - expect((oc.getSocket() as MockSecureSocket).destroyed, true); + expect((oc.underlying as MockSecureSocket).destroyed, true); expect(oc.metaData?.isClosed, true); }); @@ -123,17 +123,17 @@ void main() { OutboundConnection oc = OutboundConnectionImpl( createMockAtServerSocket('test.test.test', 12345)); OutboundMessageListener oml = OutboundMessageListener(oc); - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); expect(oc.metaData?.isClosed, false); await oml.closeConnection(); - expect((oc.getSocket() as MockSecureSocket).destroyed, true); + expect((oc.underlying as MockSecureSocket).destroyed, true); expect(oc.metaData?.isClosed, true); - (oc.getSocket() as MockSecureSocket).destroyed = false; + (oc.underlying as MockSecureSocket).destroyed = false; await oml.closeConnection(); // Since the connection was already closed above, // we don't expect destroy to be called on the socket again - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); expect(oc.metaData?.isClosed, true); }); @@ -143,7 +143,7 @@ void main() { OutboundConnection oc = OutboundConnectionImpl( createMockAtServerSocket('test.test.test', 12345)); OutboundMessageListener oml = OutboundMessageListener(oc); - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); expect(oc.metaData?.isClosed, false); expect(oc.isInValid(), false); @@ -154,7 +154,7 @@ void main() { await oml.closeConnection(); - expect((oc.getSocket() as MockSecureSocket).destroyed, true); + expect((oc.underlying as MockSecureSocket).destroyed, true); expect(oc.metaData?.isClosed, true); }); @@ -164,13 +164,13 @@ void main() { OutboundConnection oc = OutboundConnectionImpl( createMockAtServerSocket('test.test.test', 12345)); OutboundMessageListener oml = OutboundMessageListener(oc); - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); oc.metaData!.isClosed = true; await oml.closeConnection(); // socketDestroyed will be set in these tests only if socket.destroy() is called - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); }); test( @@ -179,13 +179,13 @@ void main() { OutboundConnection oc = OutboundConnectionImpl( createMockAtServerSocket('test.test.test', 12345)); OutboundMessageListener oml = OutboundMessageListener(oc); - expect((oc.getSocket() as MockSecureSocket).destroyed, false); + expect((oc.underlying as MockSecureSocket).destroyed, false); expect(oc.metaData?.isClosed, false); oc.metaData!.isStale = true; await oml.closeConnection(); - expect((oc.getSocket() as MockSecureSocket).destroyed, true); + expect((oc.underlying as MockSecureSocket).destroyed, true); expect(oc.metaData?.isClosed, true); }); }); From caa7591edd3f47f7f8424ce37dbf034b215a0cc8 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Mon, 7 Oct 2024 19:23:17 +0530 Subject: [PATCH 02/23] websocket changes --- .../at_lookup/lib/src/at_lookup_impl.dart | 2 +- .../lib/src/util/secure_socket_util.dart | 35 ++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 2ee18d95..8124221c 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -285,7 +285,7 @@ class AtLookupImpl implements AtLookUp { await createOutBoundWebSocketConnection(host, port.toString(), _currentAtSign, _secureSocketConfig); messageListener = socketListenerFactory - .createWebSocketListener(_connection as OutboundWebSocketConnection); + .createWebSocketListener(_webSocketConnection!); messageListener.listen(); logger.info('New connection created OK'); } diff --git a/packages/at_lookup/lib/src/util/secure_socket_util.dart b/packages/at_lookup/lib/src/util/secure_socket_util.dart index 17e0972d..22488e73 100644 --- a/packages/at_lookup/lib/src/util/secure_socket_util.dart +++ b/packages/at_lookup/lib/src/util/secure_socket_util.dart @@ -1,4 +1,7 @@ +import 'dart:async'; +import 'dart:convert'; import 'dart:io'; +import 'dart:math'; import 'package:at_commons/at_commons.dart'; class SecureSocketUtil { @@ -14,12 +17,36 @@ class SecureSocketUtil { } } - /// Creates a secure WebSocket connection. - static Future createSecureWebSocket( + +static Future createSecureWebSocket( String host, String port, SecureSocketConfig secureSocketConfig) async { try { - final uri = Uri.parse('wss://$host:$port'); - WebSocket webSocket = await WebSocket.connect(uri.toString()); + SecureSocket socket = await SecureSocket.connect( + host, + int.parse(port), + supportedProtocols: ['http/1.1'], // Request 'http/1.1' during ALPN + ); + + if (socket.selectedProtocol != 'http/1.1') { + throw AtException('Failed to negotiate http/1.1 via ALPN'); + } + + String webSocketKey = + base64.encode(List.generate(16, (_) => Random().nextInt(256))); + socket.write('GET /ws HTTP/1.1\r\n'); + socket.write('Host: $host:$port\r\n'); + socket.write('Connection: Upgrade\r\n'); + socket.write('Upgrade: websocket\r\n'); + socket.write('Sec-WebSocket-Version: 13\r\n'); + socket.write('Sec-WebSocket-Key: $webSocketKey\r\n'); + socket.write('\r\n'); + + WebSocket webSocket = WebSocket.fromUpgradedSocket( + socket, + serverSide: false, + ); + + print('WebSocket connection established'); return webSocket; } catch (e) { throw AtException('Error creating WebSocket connection: ${e.toString()}'); From 63fc299729c5d65c5587e38a24be1c5a42568225 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Wed, 16 Oct 2024 02:13:17 +0530 Subject: [PATCH 03/23] fixed closing ws connection --- .../lib/src/util/secure_socket_util.dart | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/at_lookup/lib/src/util/secure_socket_util.dart b/packages/at_lookup/lib/src/util/secure_socket_util.dart index 22488e73..d4f853ff 100644 --- a/packages/at_lookup/lib/src/util/secure_socket_util.dart +++ b/packages/at_lookup/lib/src/util/secure_socket_util.dart @@ -17,37 +17,35 @@ class SecureSocketUtil { } } - -static Future createSecureWebSocket( + static Future createSecureWebSocket( String host, String port, SecureSocketConfig secureSocketConfig) async { try { - SecureSocket socket = await SecureSocket.connect( - host, - int.parse(port), - supportedProtocols: ['http/1.1'], // Request 'http/1.1' during ALPN - ); + Random r = Random(); + String key = base64.encode(List.generate(8, (_) => r.nextInt(256))); - if (socket.selectedProtocol != 'http/1.1') { - throw AtException('Failed to negotiate http/1.1 via ALPN'); - } + SecurityContext context = SecurityContext.defaultContext; + context.setAlpnProtocols(['http/1.1'], false); + HttpClient client = HttpClient(context: context); - String webSocketKey = - base64.encode(List.generate(16, (_) => Random().nextInt(256))); - socket.write('GET /ws HTTP/1.1\r\n'); - socket.write('Host: $host:$port\r\n'); - socket.write('Connection: Upgrade\r\n'); - socket.write('Upgrade: websocket\r\n'); - socket.write('Sec-WebSocket-Version: 13\r\n'); - socket.write('Sec-WebSocket-Key: $webSocketKey\r\n'); - socket.write('\r\n'); + Uri uri = Uri.parse("https://$host:$port/ws"); + HttpClientRequest request = await client.getUrl(uri); + request.headers.add('Connection', 'upgrade'); + request.headers.add('Upgrade', 'websocket'); + request.headers.add( + 'sec-websocket-version', '13'); // insert the correct version here + request.headers.add('sec-websocket-key', key); - WebSocket webSocket = WebSocket.fromUpgradedSocket( + HttpClientResponse response = await request.close(); + Socket socket = await response.detachSocket(); + + WebSocket ws = WebSocket.fromUpgradedSocket( socket, serverSide: false, ); print('WebSocket connection established'); - return webSocket; + + return ws; } catch (e) { throw AtException('Error creating WebSocket connection: ${e.toString()}'); } From df4658d20186b50432806c83a3a9f9e106bdaa51 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Thu, 17 Oct 2024 01:00:59 +0530 Subject: [PATCH 04/23] changes --- .../at_lookup/lib/src/at_lookup_impl.dart | 48 +++++++------------ ...on.dart => base_websocket_connection.dart} | 4 +- .../outbound_websocket_connection.dart | 4 +- 3 files changed, 21 insertions(+), 35 deletions(-) rename packages/at_lookup/lib/src/connection/{base_socket_connection.dart => base_websocket_connection.dart} (93%) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 8124221c..f499ca25 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -243,29 +243,6 @@ class AtLookupImpl implements AtLookUp { } Future createConnection({bool useWebSocket = false}) async { - if (!isConnectionAvailable()) { - if (_connection != null) { - // Clean up the connection before creating a new one - logger.finer('Closing old connection'); - await _connection!.close(); - } - logger.info('Creating new connection'); - //1. find secondary url for atsign from lookup library - SecondaryAddress secondaryAddress = - await secondaryAddressFinder.findSecondary(_currentAtSign); - var host = secondaryAddress.host; - var port = secondaryAddress.port; - //2. create a connection to secondary server - await createOutBoundConnection( - host, port.toString(), _currentAtSign, _secureSocketConfig); - //3. listen to server response - messageListener = socketListenerFactory.createListener(_connection!); - messageListener.listen(); - logger.info('New connection created OK'); - } - } - - Future createWebSocketConnection() async { if (!isConnectionAvailable()) { if (_connection != null) { // Clean up the connection before creating a new one @@ -280,19 +257,26 @@ class AtLookupImpl implements AtLookUp { var host = secondaryAddress.host; var port = secondaryAddress.port; + if (useWebSocket) { // Create WebSocket connection logger.info('Using WebSocket connection'); - await createOutBoundWebSocketConnection(host, port.toString(), _currentAtSign, _secureSocketConfig); - + await createOutBoundWebSocketConnection( + host, port.toString(), _currentAtSign, _secureSocketConfig); messageListener = socketListenerFactory .createWebSocketListener(_webSocketConnection!); + } else { + // Create SecureSocket connection + await createOutBoundConnection( + host, port.toString(), _currentAtSign, _secureSocketConfig); + messageListener = socketListenerFactory.createListener(_connection!); + } + + // 3. Listen to server response messageListener.listen(); logger.info('New connection created OK'); } } - - /// Executes the command returned by [VerbBuilder] build command on a remote secondary server. /// Catches any exception and throws [AtLookUpException] @override @@ -682,8 +666,8 @@ class AtLookupImpl implements AtLookUp { try { WebSocket secureSocket = await socketFactory.createWebSocket(host, port, secureSocketConfig); - _webSocketConnection = - outboundConnectionFactory.createWebSocketOutboundConnection(secureSocket); + _webSocketConnection = outboundConnectionFactory + .createWebSocketOutboundConnection(secureSocket); if (outboundConnectionTimeout != null) { _connection!.setIdleTime(outboundConnectionTimeout); } @@ -740,7 +724,8 @@ class AtLookupSecureSocketFactory { Future createWebSocket( String host, String port, SecureSocketConfig socketConfig) async { - return await SecureSocketUtil.createSecureSocket(host, port, socketConfig, isWebSocket: true); + return await SecureSocketUtil.createSecureSocket(host, port, socketConfig, + isWebSocket: true); } } @@ -762,7 +747,8 @@ class AtLookupOutboundConnectionFactory { } // introduce new createWebSocketCOnnection - OutboundWebSocketConnection createWebSocketOutboundConnection(WebSocket webSocket) { + OutboundWebSocketConnection createWebSocketOutboundConnection( + WebSocket webSocket) { return OutboundWebsocketConnectionImpl(webSocket); } } diff --git a/packages/at_lookup/lib/src/connection/base_socket_connection.dart b/packages/at_lookup/lib/src/connection/base_websocket_connection.dart similarity index 93% rename from packages/at_lookup/lib/src/connection/base_socket_connection.dart rename to packages/at_lookup/lib/src/connection/base_websocket_connection.dart index d4695330..91781b34 100644 --- a/packages/at_lookup/lib/src/connection/base_socket_connection.dart +++ b/packages/at_lookup/lib/src/connection/base_websocket_connection.dart @@ -4,13 +4,13 @@ import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_utils/at_logger.dart'; /// WebSocket-specific connection class -abstract class BaseSocketConnection extends AtConnection { +abstract class BaseWebSocketConnection extends AtConnection { final WebSocket _webSocket; late final AtSignLogger logger; StringBuffer? buffer; AtConnectionMetaData? metaData; - BaseSocketConnection(this._webSocket) { + BaseWebSocketConnection(this._webSocket) { logger = AtSignLogger(runtimeType.toString()); buffer = StringBuffer(); } diff --git a/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart b/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart index da3ac185..1b6027c6 100644 --- a/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart +++ b/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart @@ -1,8 +1,8 @@ import 'dart:io'; -import 'package:at_lookup/src/connection/base_socket_connection.dart'; +import 'package:at_lookup/src/connection/base_websocket_connection.dart'; -abstract class OutboundWebSocketConnection extends BaseSocketConnection { +abstract class OutboundWebSocketConnection extends BaseWebSocketConnection { final WebSocket webSocket; OutboundWebSocketConnection(this.webSocket) : super(webSocket); From 417234cb49de8a9026c3375e889aaf35a03962e2 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Wed, 23 Oct 2024 04:11:42 +0530 Subject: [PATCH 05/23] refactored code --- .../at_lookup/lib/src/at_lookup_impl.dart | 160 ++++++++++++------ .../connection/outbound_message_listener.dart | 50 ++++-- .../test/outbound_message_listener_test.dart | 98 +++++------ 3 files changed, 196 insertions(+), 112 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index f499ca25..46cc45d1 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -5,15 +5,16 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:at_chops/at_chops.dart'; import 'package:at_commons/at_builders.dart'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; import 'package:at_utils/at_logger.dart'; import 'package:crypto/crypto.dart'; import 'package:crypton/crypton.dart'; import 'package:mutex/mutex.dart'; -import 'package:at_chops/at_chops.dart'; class AtLookupImpl implements AtLookUp { final logger = AtSignLogger('AtLookup'); @@ -21,13 +22,16 @@ class AtLookupImpl implements AtLookUp { /// Listener for reading verb responses from the remote server late OutboundMessageListener messageListener; + /// Stores whether WebSocket should be used for the connection + final bool useWebSocket; + OutboundConnection? _connection; OutboundConnection? get connection => _connection; OutboundWebSocketConnection? _webSocketConnection; - // OutboundWebSocketConnection? get _webSocketConnection => _webSocketConnection; + OutboundWebSocketConnection? get webSocketConnection => _webSocketConnection; @override late SecondaryAddressFinder secondaryAddressFinder; @@ -50,10 +54,17 @@ class AtLookupImpl implements AtLookUp { late final AtLookupSecureSocketFactory socketFactory; + late AtLookupWebSocketFactory webSocketFactory; + late final AtLookupSecureSocketListenerFactory socketListenerFactory; + late final AtLookupWebSocketListenerFactory webSocketListenerFactory; + late AtLookupOutboundConnectionFactory outboundConnectionFactory; + late AtLookupWebsocketOutBoundConnectionFactory + outboundWebsocketConnectionFactory; + /// Represents the client configurations. late Map _clientConfig; @@ -67,7 +78,13 @@ class AtLookupImpl implements AtLookUp { Map? clientConfig, AtLookupSecureSocketFactory? secureSocketFactory, AtLookupSecureSocketListenerFactory? socketListenerFactory, - AtLookupOutboundConnectionFactory? outboundConnectionFactory}) { + AtLookupOutboundConnectionFactory? outboundConnectionFactory, + AtLookupWebSocketFactory? webSocketFactory, + AtLookupWebSocketListenerFactory? webSocketListenerFactory, + AtLookupWebsocketOutBoundConnectionFactory? + outboundWebSocketConnectionFactory, + this.useWebSocket = false // Set default to false + }) { _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; @@ -78,10 +95,16 @@ class AtLookupImpl implements AtLookUp { // If client configurations are not available, defaults to empty map _clientConfig = clientConfig ?? {}; socketFactory = secureSocketFactory ?? AtLookupSecureSocketFactory(); + // Set defaults if no factories are provided + this.webSocketFactory = webSocketFactory ?? AtLookupWebSocketFactory(); this.socketListenerFactory = socketListenerFactory ?? AtLookupSecureSocketListenerFactory(); this.outboundConnectionFactory = outboundConnectionFactory ?? AtLookupOutboundConnectionFactory(); + this.webSocketListenerFactory = + webSocketListenerFactory ?? AtLookupWebSocketListenerFactory(); + outboundWebsocketConnectionFactory = outboundWebSocketConnectionFactory ?? + AtLookupWebsocketOutBoundConnectionFactory(); } @Deprecated('use CacheableSecondaryAddressFinder') @@ -242,7 +265,7 @@ class AtLookupImpl implements AtLookUp { return putResult.isNotEmpty; } - Future createConnection({bool useWebSocket = false}) async { + Future createConnection() async { if (!isConnectionAvailable()) { if (_connection != null) { // Clean up the connection before creating a new one @@ -257,19 +280,9 @@ class AtLookupImpl implements AtLookUp { var host = secondaryAddress.host; var port = secondaryAddress.port; - if (useWebSocket) { - // Create WebSocket connection - logger.info('Using WebSocket connection'); - await createOutBoundWebSocketConnection( - host, port.toString(), _currentAtSign, _secureSocketConfig); - messageListener = socketListenerFactory - .createWebSocketListener(_webSocketConnection!); - } else { - // Create SecureSocket connection - await createOutBoundConnection( - host, port.toString(), _currentAtSign, _secureSocketConfig); - messageListener = socketListenerFactory.createListener(_connection!); - } + // 2. Create a connection to the secondary server + await createOutboundConnection( + host, port.toString(), _secureSocketConfig); // 3. Listen to server response messageListener.listen(); @@ -489,6 +502,7 @@ class AtLookupImpl implements AtLookUp { @override Future pkamAuthenticate({String? enrollmentId}) async { await createConnection(); + final _connection = _getCurrentConnection(); try { await _pkamAuthenticationMutex.acquire(); if (!_connection!.getMetaData()!.isAuthenticated) { @@ -521,13 +535,13 @@ class AtLookupImpl implements AtLookUp { var pkamResponse = await messageListener.read(); if (pkamResponse == 'data:success') { logger.info('auth success'); - _connection!.getMetaData()!.isAuthenticated = true; + _connection.getMetaData()!.isAuthenticated = true; } else { throw UnAuthenticatedException( 'Failed connecting to $_currentAtSign. $pkamResponse'); } } - return _connection!.getMetaData()!.isAuthenticated; + return _connection.getMetaData()!.isAuthenticated; } finally { _pkamAuthenticationMutex.release(); } @@ -538,34 +552,44 @@ class AtLookupImpl implements AtLookUp { @override Future cramAuthenticate(String secret) async { await createConnection(); + final _connection = _getCurrentConnection(); try { await _cramAuthenticationMutex.acquire(); + if (!_connection!.getMetaData()!.isAuthenticated) { + // Use the connection and message listener dynamically await _sendCommand((FromVerbBuilder() ..atSign = _currentAtSign ..clientConfig = _clientConfig) .buildCommand()); + var fromResponse = await messageListener.read( transientWaitTimeMillis: 4000, maxWaitMilliSeconds: 10000); logger.info('from result:$fromResponse'); + if (fromResponse.isEmpty) { return false; } + fromResponse = fromResponse.trim().replaceAll('data:', ''); + var digestInput = '$secret$fromResponse'; var bytes = utf8.encode(digestInput); var digest = sha512.convert(bytes); + await _sendCommand('cram:$digest\n'); var cramResponse = await messageListener.read( transientWaitTimeMillis: 4000, maxWaitMilliSeconds: 10000); + if (cramResponse == 'data:success') { logger.info('auth success'); - _connection!.getMetaData()!.isAuthenticated = true; + _connection.getMetaData()!.isAuthenticated = true; } else { throw UnAuthenticatedException('Auth failed'); } } - return _connection!.getMetaData()!.isAuthenticated; + + return _connection.getMetaData()!.isAuthenticated; } finally { _cramAuthenticationMutex.release(); } @@ -640,61 +664,89 @@ class AtLookupImpl implements AtLookUp { } bool _isAuthRequired() { - return !isConnectionAvailable() || - !(_connection!.getMetaData()!.isAuthenticated); + // Get the current active connection (WebSocket or regular). + final connection = _getCurrentConnection(); + + // If there is no connection or the connection is not authenticated, auth is required. + return connection == null || !connection.getMetaData()!.isAuthenticated; } - Future createOutBoundConnection(String host, String port, - String toAtSign, SecureSocketConfig secureSocketConfig) async { + Future createOutboundConnection( + String host, String port, SecureSocketConfig secureSocketConfig) async { try { - SecureSocket secureSocket = - await socketFactory.createSocket(host, port, secureSocketConfig); - _connection = - outboundConnectionFactory.createOutboundConnection(secureSocket); - if (outboundConnectionTimeout != null) { - _connection!.setIdleTime(outboundConnectionTimeout); + if (useWebSocket) { + // Create WebSocket connection + WebSocket webSocket = await webSocketFactory.createWebSocket( + host, port, secureSocketConfig); + _webSocketConnection = outboundWebsocketConnectionFactory + .createWebSocketOutboundConnection(webSocket); + if (outboundConnectionTimeout != null) { + _webSocketConnection!.setIdleTime(outboundConnectionTimeout); + } + // Initialize the WebSocket listener + messageListener = webSocketListenerFactory + .createWebSocketListener(_webSocketConnection!); + } else { + // Create SecureSocket connection + SecureSocket secureSocket = + await socketFactory.createSocket(host, port, secureSocketConfig); + _connection = + outboundConnectionFactory.createOutboundConnection(secureSocket); + if (outboundConnectionTimeout != null) { + _connection!.setIdleTime(outboundConnectionTimeout); + } + // Initialize the SecureSocket listener + messageListener = socketListenerFactory.createListener(_connection!); } } on SocketException { throw SecondaryConnectException( - 'unable to connect to secondary $toAtSign on $host:$port'); + 'Unable to connect to secondary $_currentAtSign on $host:$port'); } return true; } - Future createOutBoundWebSocketConnection(String host, String port, - String toAtSign, SecureSocketConfig secureSocketConfig) async { - try { - WebSocket secureSocket = - await socketFactory.createWebSocket(host, port, secureSocketConfig); - _webSocketConnection = outboundConnectionFactory - .createWebSocketOutboundConnection(secureSocket); - if (outboundConnectionTimeout != null) { - _connection!.setIdleTime(outboundConnectionTimeout); - } - } on SocketException { - throw SecondaryConnectException( - 'unable to connect to secondary $toAtSign on $host:$port'); - } - return true; + /// Helper method to get the current active connection (either WebSocket or regular). + AtConnection? _getCurrentConnection() { + return useWebSocket ? _webSocketConnection : _connection; } bool isConnectionAvailable() { - return _connection != null && !_connection!.isInValid(); + // Get the current active connection (WebSocket or regular). + final connection = _getCurrentConnection(); + + // Check if the connection is not null and valid. + return connection != null && !connection.isInValid(); } bool isInValid() { - return _connection!.isInValid(); + // Get the current active connection (WebSocket or regular). + final connection = _getCurrentConnection(); + + // If no connection is available, consider it invalid. + if (connection == null) { + return true; + } + + // Check if the connection is invalid based on its state. + return connection.isInValid(); } @override Future close() async { - await _connection!.close(); + // Get the current active connection (WebSocket or regular). + final connection = _getCurrentConnection(); + + // If there's an active connection, close it. + if (connection != null) { + await connection.close(); + } } Future _sendCommand(String command) async { await createConnection(); logger.finer('SENDING: $command'); - await _connection!.write(command); + final connection = _getCurrentConnection(); + connection!.write(command); } @override @@ -721,7 +773,9 @@ class AtLookupSecureSocketFactory { String host, String port, SecureSocketConfig socketConfig) async { return await SecureSocketUtil.createSecureSocket(host, port, socketConfig); } +} +class AtLookupWebSocketFactory { Future createWebSocket( String host, String port, SecureSocketConfig socketConfig) async { return await SecureSocketUtil.createSecureSocket(host, port, socketConfig, @@ -734,7 +788,9 @@ class AtLookupSecureSocketListenerFactory { OutboundConnection outboundConnection) { return OutboundMessageListener(outboundConnection); } +} +class AtLookupWebSocketListenerFactory { OutboundMessageListener createWebSocketListener( OutboundWebSocketConnection outboundWebSocketConnection) { return OutboundMessageListener(outboundWebSocketConnection); @@ -745,7 +801,9 @@ class AtLookupOutboundConnectionFactory { OutboundConnection createOutboundConnection(SecureSocket secureSocket) { return OutboundConnectionImpl(secureSocket); } +} +class AtLookupWebsocketOutBoundConnectionFactory { // introduce new createWebSocketCOnnection OutboundWebSocketConnection createWebSocketOutboundConnection( WebSocket webSocket) { diff --git a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart b/packages/at_lookup/lib/src/connection/outbound_message_listener.dart index 6d90386f..4e7b7e4e 100644 --- a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart +++ b/packages/at_lookup/lib/src/connection/outbound_message_listener.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; +import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; @@ -8,7 +9,7 @@ import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; -///Listener class for messages received by [RemoteSecondary] +/// Listener class for messages received by [RemoteSecondary] class OutboundMessageListener { final logger = AtSignLogger('OutboundMessageListener'); late ByteBuffer _buffer; @@ -23,16 +24,31 @@ class OutboundMessageListener { _buffer = ByteBuffer(capacity: bufferCapacity); } - /// Listens to the underlying connection's socket if the connection is created. + /// Listens to the underlying connection's socket (either WebSocket or raw socket) + /// if the connection is created. /// @throws [AtConnectException] if the connection is not yet created void listen() { logger.finest('Calling socket.listen within runZonedGuarded block'); runZonedGuarded(() { - _connection - .underlying - .listen(messageHandler, onDone: onSocketDone, onError: onSocketError); + // Check if the underlying connection is a WebSocket or a raw socket + if (_connection.underlying is WebSocket) { + (_connection.underlying as WebSocket).listen( + (dynamic message) => messageHandler(message), + onDone: onSocketDone, + onError: onSocketError, + ); + } else if (_connection.underlying is Socket) { + (_connection.underlying as Socket).listen( + (data) => messageHandler(data), + onDone: onSocketDone, + onError: onSocketError, + ); + } else { + throw UnsupportedError('Unsupported connection type'); + } }, (Object error, StackTrace st) { + logger.finer('stack trace $st'); logger.warning( 'runZonedGuarded received socket error $error - calling onSocketError() to close connection'); onSocketError(error); @@ -62,13 +78,23 @@ class OutboundMessageListener { /// Handles messages on the inbound client's connection and calls the verb executor /// Closes the inbound connection in case of any error. - /// Throw a [BufferOverFlowException] if buffer is unable to hold incoming data - Future messageHandler(List data) async { + void messageHandler(dynamic data) { String result; int offset; _lastReceivedTime = DateTime.now(); - // check buffer overflow + + // If data is a String (from WebSocket), process it directly as UTF-8 encoded text. + if (data is String) { + logger.finer('WebSocket received string data: $data'); + data = utf8.encode(data); // Convert the WebSocket message to byte array + } else if (data is! List) { + logger.warning('Received unexpected data type: ${data.runtimeType}'); + return; // Exit if the data is neither String nor List + } + + // At this point, data is guaranteed to be List for both WebSocket and raw socket _checkBufferOverFlow(data); + // If the data contains a new line character, add until the new line char to buffer if (data.contains(newLineCodeUnit)) { offset = data.lastIndexOf(newLineCodeUnit); @@ -93,7 +119,7 @@ class OutboundMessageListener { result = _stripPrompt(result); logger.finer('RECEIVED $result'); _queue.add(result); - //clear the buffer after adding result to queue + // Clear the buffer after adding result to queue _buffer.clear(); _buffer.addByte(data[element]); } else { @@ -105,9 +131,9 @@ class OutboundMessageListener { /// The methods verifies if buffer has the capacity to accept the data. /// /// Throw BufferOverFlowException if data length exceeds the buffer capacity - _checkBufferOverFlow(data) { + void _checkBufferOverFlow(List data) { if (_buffer.isOverFlow(data)) { - int bufferLength = (_buffer.length() + data.length) as int; + int bufferLength = (_buffer.length() + data.length); _buffer.clear(); throw BufferOverFlowException( 'data length exceeded the buffer limit. Data length : $bufferLength and Buffer capacity ${_buffer.capacity}'); @@ -166,7 +192,7 @@ class OutboundMessageListener { _buffer.clear(); await closeConnection(); throw AtTimeoutException( - 'Waited for $transientWaitTimeMillis millis. No response after $_lastReceivedTime '); + 'Waited for $transientWaitTimeMillis millis. No response after $_lastReceivedTime'); } // wait for 10 ms before attempting to read from queue again await Future.delayed(Duration(milliseconds: 10)); diff --git a/packages/at_lookup/test/outbound_message_listener_test.dart b/packages/at_lookup/test/outbound_message_listener_test.dart index a2c7a94c..5c575414 100644 --- a/packages/at_lookup/test/outbound_message_listener_test.dart +++ b/packages/at_lookup/test/outbound_message_listener_test.dart @@ -15,7 +15,7 @@ void main() { OutboundMessageListener outboundMessageListener = OutboundMessageListener(mockOutBoundConnection); test('A test to validate complete data comes in single packet', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:phone@alice\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:phone@alice'); @@ -24,20 +24,20 @@ void main() { test( 'A test to validate complete data comes in packet and prompt in different packet', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:@bob:phone@alice\n'.codeUnits); - await outboundMessageListener.messageHandler('@alice@'.codeUnits); + outboundMessageListener.messageHandler('@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:@bob:phone@alice'); }); test('A test to validate data two complete data comes in single packets', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:@bob:phone@alice\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:@bob:phone@alice'); - await outboundMessageListener + outboundMessageListener .messageHandler('data:public:phone@alice\n@alice@'.codeUnits); response = await outboundMessageListener.read(); expect(response, 'data:public:phone@alice'); @@ -45,27 +45,27 @@ void main() { test('A test to validate data two complete data comes in multiple packets', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:public:phone@alice\n@ali'.codeUnits); - await outboundMessageListener.messageHandler('ce@'.codeUnits); + outboundMessageListener.messageHandler('ce@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:public:phone@alice'); - await outboundMessageListener.messageHandler( + outboundMessageListener.messageHandler( 'data:@bob:location@alice,@bob:phone@alice\n@alice@'.codeUnits); response = await outboundMessageListener.read(); expect(response, 'data:@bob:location@alice,@bob:phone@alice'); }); test('A test to validate single data comes two packets', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:public:phone@'.codeUnits); - await outboundMessageListener.messageHandler('alice\n@alice@'.codeUnits); + outboundMessageListener.messageHandler('alice\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:public:phone@alice'); }); test('A test to validate data contains @', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:phone@alice_12345675\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:phone@alice_12345675'); @@ -75,25 +75,25 @@ void main() { 'A test to validate data contains @ and partial prompt of previous data', () async { // partial response of previous data. - await outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); - await outboundMessageListener.messageHandler('alice@'.codeUnits); + outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); + outboundMessageListener.messageHandler('alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:hello'); - await outboundMessageListener + outboundMessageListener .messageHandler('data:phone@alice_12345675\n@alice@'.codeUnits); response = await outboundMessageListener.read(); expect(response, 'data:phone@alice_12345675'); }); test('A test to validate data contains new line character', () async { - await outboundMessageListener.messageHandler( + outboundMessageListener.messageHandler( 'data:value_contains_\nin_the_value\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:value_contains_\nin_the_value'); }); test('A test to validate data contains new line character and @', () async { - await outboundMessageListener.messageHandler( + outboundMessageListener.messageHandler( 'data:the_key_is\n@bob:phone@alice\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:the_key_is\n@bob:phone@alice'); @@ -104,17 +104,17 @@ void main() { OutboundMessageListener outboundMessageListener = OutboundMessageListener(mockOutBoundConnection); test('A test to validate response from unauthorized connection', () async { - await outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); + outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:hello'); }); test('A test to validate multiple response from unauthorized connection', () async { - await outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); + outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:hello'); - await outboundMessageListener.messageHandler('data:hi\n@'.codeUnits); + outboundMessageListener.messageHandler('data:hi\n@'.codeUnits); response = await outboundMessageListener.read(); expect(response, 'data:hi'); }); @@ -122,13 +122,13 @@ void main() { test( 'A test to validate response from unauthorized connection in multiple packets', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:public:location@alice,'.codeUnits); - await outboundMessageListener + outboundMessageListener .messageHandler('public:phone@alice\n@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'data:public:location@alice,public:phone@alice'); - await outboundMessageListener.messageHandler('data:hi\n@'.codeUnits); + outboundMessageListener.messageHandler('data:hi\n@'.codeUnits); response = await outboundMessageListener.read(); expect(response, 'data:hi'); }); @@ -166,14 +166,14 @@ void main() { OutboundMessageListener outboundMessageListener = OutboundMessageListener(mockOutBoundConnection); test('A test to validate complete error comes in single packet', () async { - await outboundMessageListener.messageHandler( + outboundMessageListener.messageHandler( 'error:AT0012: Invalid value found\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'error:AT0012: Invalid value found'); }); test('A test to validate complete error comes in single packet', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('stream:@bob:phone@alice\n@alice@'.codeUnits); var response = await outboundMessageListener.read(); expect(response, 'stream:@bob:phone@alice'); @@ -215,9 +215,9 @@ void main() { test( 'A test to verify partial response - wait time greater than transientWaitTimeMillis', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:public:phone@'.codeUnits); - await outboundMessageListener.messageHandler('12'.codeUnits); + outboundMessageListener.messageHandler('12'.codeUnits); expect( () async => await outboundMessageListener.read(transientWaitTimeMillis: 50), @@ -229,12 +229,12 @@ void main() { test( 'A test to verify partial response - wait time greater than maxWaitMillis', () async { - await outboundMessageListener + outboundMessageListener .messageHandler('data:public:phone@'.codeUnits); - await outboundMessageListener.messageHandler('12'.codeUnits); - await outboundMessageListener.messageHandler('34'.codeUnits); - await outboundMessageListener.messageHandler('56'.codeUnits); - await outboundMessageListener.messageHandler('78'.codeUnits); + outboundMessageListener.messageHandler('12'.codeUnits); + outboundMessageListener.messageHandler('34'.codeUnits); + outboundMessageListener.messageHandler('56'.codeUnits); + outboundMessageListener.messageHandler('78'.codeUnits); expect( () async => // we want to trigger the maxWaitMilliSeconds exception, so setting transient to a higher value @@ -253,17 +253,17 @@ void main() { .read(transientWaitTimeMillis: 50) .whenComplete(() => {}) .then((value) => response = value)); - await outboundMessageListener.messageHandler('data:'.codeUnits); + outboundMessageListener.messageHandler('data:'.codeUnits); await Future.delayed(Duration(milliseconds: 25)); - await outboundMessageListener.messageHandler('12'.codeUnits); + outboundMessageListener.messageHandler('12'.codeUnits); await Future.delayed(Duration(milliseconds: 15)); - await outboundMessageListener.messageHandler('34'.codeUnits); + outboundMessageListener.messageHandler('34'.codeUnits); await Future.delayed(Duration(milliseconds: 17)); - await outboundMessageListener.messageHandler('56'.codeUnits); + outboundMessageListener.messageHandler('56'.codeUnits); await Future.delayed(Duration(milliseconds: 30)); - await outboundMessageListener.messageHandler('78'.codeUnits); + outboundMessageListener.messageHandler('78'.codeUnits); await Future.delayed(Duration(milliseconds: 45)); - await outboundMessageListener.messageHandler('910\n@'.codeUnits); + outboundMessageListener.messageHandler('910\n@'.codeUnits); await Future.delayed(Duration(milliseconds: 25)); expect(response, isNotEmpty); expect(response, 'data:12345678910'); @@ -279,17 +279,17 @@ void main() { }) .whenComplete(() => {}) .then((value) => {response = value}); - await outboundMessageListener.messageHandler('data:'.codeUnits); + outboundMessageListener.messageHandler('data:'.codeUnits); await Future.delayed(Duration(milliseconds: 15)); - await outboundMessageListener.messageHandler('12'.codeUnits); + outboundMessageListener.messageHandler('12'.codeUnits); await Future.delayed(Duration(milliseconds: 10)); - await outboundMessageListener.messageHandler('34'.codeUnits); + outboundMessageListener.messageHandler('34'.codeUnits); await Future.delayed(Duration(milliseconds: 12)); - await outboundMessageListener.messageHandler('56'.codeUnits); + outboundMessageListener.messageHandler('56'.codeUnits); await Future.delayed(Duration(milliseconds: 13)); - await outboundMessageListener.messageHandler('78'.codeUnits); + outboundMessageListener.messageHandler('78'.codeUnits); await Future.delayed(Duration(milliseconds: 20)); - await outboundMessageListener.messageHandler('910'.codeUnits); + outboundMessageListener.messageHandler('910'.codeUnits); await Future.delayed(Duration(milliseconds: 50)); expect(response, isNotEmpty); expect( @@ -309,17 +309,17 @@ void main() { }) .whenComplete(() => {}) .then((value) => {response = value}); - await outboundMessageListener.messageHandler('data:'.codeUnits); + outboundMessageListener.messageHandler('data:'.codeUnits); await Future.delayed(Duration(milliseconds: 10)); - await outboundMessageListener.messageHandler('12'.codeUnits); + outboundMessageListener.messageHandler('12'.codeUnits); await Future.delayed(Duration(milliseconds: 15)); - await outboundMessageListener.messageHandler('34'.codeUnits); + outboundMessageListener.messageHandler('34'.codeUnits); await Future.delayed(Duration(milliseconds: 17)); - await outboundMessageListener.messageHandler('56'.codeUnits); + outboundMessageListener.messageHandler('56'.codeUnits); await Future.delayed(Duration(milliseconds: 20)); - await outboundMessageListener.messageHandler('78'.codeUnits); + outboundMessageListener.messageHandler('78'.codeUnits); await Future.delayed(Duration(milliseconds: 10)); - await outboundMessageListener.messageHandler('910'.codeUnits); + outboundMessageListener.messageHandler('910'.codeUnits); await Future.delayed(Duration(milliseconds: 60)); expect(response, isNotEmpty); expect( From b78716add376de29fb9da6f0a9f9920f43f22335 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Tue, 5 Nov 2024 01:39:38 +0530 Subject: [PATCH 06/23] refactored code based on factories --- packages/at_lookup/lib/at_lookup.dart | 2 +- .../at_lookup/lib/src/at_lookup_impl.dart | 136 +++++------------- .../cacheable_secondary_address_finder.dart | 11 +- .../src/connection/at_connection_factory.dart | 79 ++++++++++ .../lib/src/util/secure_socket_util.dart | 4 +- .../test/at_lookup_test.dart | 96 +++++++++++++ 6 files changed, 216 insertions(+), 112 deletions(-) create mode 100644 packages/at_lookup/lib/src/connection/at_connection_factory.dart create mode 100644 tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart diff --git a/packages/at_lookup/lib/at_lookup.dart b/packages/at_lookup/lib/at_lookup.dart index e520c1a6..0d37d816 100644 --- a/packages/at_lookup/lib/at_lookup.dart +++ b/packages/at_lookup/lib/at_lookup.dart @@ -13,4 +13,4 @@ export 'src/monitor_client.dart'; export 'src/cache/secondary_address_finder.dart'; export 'src/cache/cacheable_secondary_address_finder.dart'; export 'src/util/secure_socket_util.dart'; -export 'src/connection/outbound_websocket_connection_impl.dart'; +export 'src/connection/outbound_websocket_connection_impl.dart'; \ No newline at end of file diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 46cc45d1..09ccd8a0 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -16,15 +16,14 @@ import 'package:crypto/crypto.dart'; import 'package:crypton/crypton.dart'; import 'package:mutex/mutex.dart'; +import 'connection/at_connection_factory.dart'; + class AtLookupImpl implements AtLookUp { final logger = AtSignLogger('AtLookup'); /// Listener for reading verb responses from the remote server late OutboundMessageListener messageListener; - /// Stores whether WebSocket should be used for the connection - final bool useWebSocket; - OutboundConnection? _connection; OutboundConnection? get connection => _connection; @@ -33,6 +32,8 @@ class AtLookupImpl implements AtLookUp { OutboundWebSocketConnection? get webSocketConnection => _webSocketConnection; + late AtConnectionFactory atConnectionFactory; + @override late SecondaryAddressFinder secondaryAddressFinder; @@ -52,19 +53,6 @@ class AtLookupImpl implements AtLookUp { late SecureSocketConfig _secureSocketConfig; - late final AtLookupSecureSocketFactory socketFactory; - - late AtLookupWebSocketFactory webSocketFactory; - - late final AtLookupSecureSocketListenerFactory socketListenerFactory; - - late final AtLookupWebSocketListenerFactory webSocketListenerFactory; - - late AtLookupOutboundConnectionFactory outboundConnectionFactory; - - late AtLookupWebsocketOutBoundConnectionFactory - outboundWebsocketConnectionFactory; - /// Represents the client configurations. late Map _clientConfig; @@ -76,15 +64,12 @@ class AtLookupImpl implements AtLookUp { SecondaryAddressFinder? secondaryAddressFinder, SecureSocketConfig? secureSocketConfig, Map? clientConfig, - AtLookupSecureSocketFactory? secureSocketFactory, - AtLookupSecureSocketListenerFactory? socketListenerFactory, - AtLookupOutboundConnectionFactory? outboundConnectionFactory, - AtLookupWebSocketFactory? webSocketFactory, - AtLookupWebSocketListenerFactory? webSocketListenerFactory, - AtLookupWebsocketOutBoundConnectionFactory? - outboundWebSocketConnectionFactory, - this.useWebSocket = false // Set default to false - }) { + bool useWebSocket = false}) // Add a flag for WebSocket or SecureSocket + { + // Determine the factory type based on the useWebSocket flag + atConnectionFactory = useWebSocket + ? WebSocketFactory() + : SecureSocketFactory() as AtConnectionFactory; _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; @@ -94,17 +79,6 @@ class AtLookupImpl implements AtLookUp { // Stores the client configurations. // If client configurations are not available, defaults to empty map _clientConfig = clientConfig ?? {}; - socketFactory = secureSocketFactory ?? AtLookupSecureSocketFactory(); - // Set defaults if no factories are provided - this.webSocketFactory = webSocketFactory ?? AtLookupWebSocketFactory(); - this.socketListenerFactory = - socketListenerFactory ?? AtLookupSecureSocketListenerFactory(); - this.outboundConnectionFactory = - outboundConnectionFactory ?? AtLookupOutboundConnectionFactory(); - this.webSocketListenerFactory = - webSocketListenerFactory ?? AtLookupWebSocketListenerFactory(); - outboundWebsocketConnectionFactory = outboundWebSocketConnectionFactory ?? - AtLookupWebsocketOutBoundConnectionFactory(); } @Deprecated('use CacheableSecondaryAddressFinder') @@ -674,29 +648,26 @@ class AtLookupImpl implements AtLookUp { Future createOutboundConnection( String host, String port, SecureSocketConfig secureSocketConfig) async { try { - if (useWebSocket) { - // Create WebSocket connection - WebSocket webSocket = await webSocketFactory.createWebSocket( - host, port, secureSocketConfig); - _webSocketConnection = outboundWebsocketConnectionFactory - .createWebSocketOutboundConnection(webSocket); - if (outboundConnectionTimeout != null) { - _webSocketConnection!.setIdleTime(outboundConnectionTimeout); - } - // Initialize the WebSocket listener - messageListener = webSocketListenerFactory - .createWebSocketListener(_webSocketConnection!); - } else { - // Create SecureSocket connection - SecureSocket secureSocket = - await socketFactory.createSocket(host, port, secureSocketConfig); - _connection = - outboundConnectionFactory.createOutboundConnection(secureSocket); - if (outboundConnectionTimeout != null) { - _connection!.setIdleTime(outboundConnectionTimeout); - } - // Initialize the SecureSocket listener - messageListener = socketListenerFactory.createListener(_connection!); + // Create the socket connection using the factory + final connection = + await atConnectionFactory.create(host, port, secureSocketConfig); + + // Create the outbound connection and listener using the factory's methods + final outboundConnection = + atConnectionFactory.outBoundConnectionFactory(connection); + messageListener = atConnectionFactory.listenerFactory(outboundConnection); + + // Set the connection type in `_webSocketConnection` or `_connection` + if (connection is WebSocket) { + _webSocketConnection = + outboundConnection as OutboundWebSocketConnection; + } else if (connection is SecureSocket) { + _connection = outboundConnection as OutboundConnection; + } + + // Set idle time if applicable + if (outboundConnectionTimeout != null) { + outboundConnection.setIdleTime(outboundConnectionTimeout); } } on SocketException { throw SecondaryConnectException( @@ -707,7 +678,7 @@ class AtLookupImpl implements AtLookUp { /// Helper method to get the current active connection (either WebSocket or regular). AtConnection? _getCurrentConnection() { - return useWebSocket ? _webSocketConnection : _connection; + return _webSocketConnection ?? _connection; } bool isConnectionAvailable() { @@ -745,7 +716,7 @@ class AtLookupImpl implements AtLookUp { Future _sendCommand(String command) async { await createConnection(); logger.finer('SENDING: $command'); - final connection = _getCurrentConnection(); + final connection = _getCurrentConnection(); connection!.write(command); } @@ -767,46 +738,3 @@ class AtLookupImpl implements AtLookUp { @override String? enrollmentId; } - -class AtLookupSecureSocketFactory { - Future createSocket( - String host, String port, SecureSocketConfig socketConfig) async { - return await SecureSocketUtil.createSecureSocket(host, port, socketConfig); - } -} - -class AtLookupWebSocketFactory { - Future createWebSocket( - String host, String port, SecureSocketConfig socketConfig) async { - return await SecureSocketUtil.createSecureSocket(host, port, socketConfig, - isWebSocket: true); - } -} - -class AtLookupSecureSocketListenerFactory { - OutboundMessageListener createListener( - OutboundConnection outboundConnection) { - return OutboundMessageListener(outboundConnection); - } -} - -class AtLookupWebSocketListenerFactory { - OutboundMessageListener createWebSocketListener( - OutboundWebSocketConnection outboundWebSocketConnection) { - return OutboundMessageListener(outboundWebSocketConnection); - } -} - -class AtLookupOutboundConnectionFactory { - OutboundConnection createOutboundConnection(SecureSocket secureSocket) { - return OutboundConnectionImpl(secureSocket); - } -} - -class AtLookupWebsocketOutBoundConnectionFactory { -// introduce new createWebSocketCOnnection - OutboundWebSocketConnection createWebSocketOutboundConnection( - WebSocket webSocket) { - return OutboundWebsocketConnectionImpl(webSocket); - } -} diff --git a/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart b/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart index dc74cef9..574288e3 100644 --- a/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart +++ b/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; +import 'package:at_lookup/src/connection/at_connection_factory.dart'; import 'package:at_lookup/src/util/lookup_util.dart'; import 'package:at_utils/at_logger.dart'; @@ -128,11 +129,11 @@ class SecondaryAddressCacheEntry { class SecondaryUrlFinder { final String _rootDomain; final int _rootPort; - late final AtLookupSecureSocketFactory _socketFactory; + late final AtConnectionFactory _socketFactory; SecondaryUrlFinder(this._rootDomain, this._rootPort, - {AtLookupSecureSocketFactory? socketFactory}) { - _socketFactory = socketFactory ?? AtLookupSecureSocketFactory(); + {AtConnectionFactory? socketFactory}) { + _socketFactory = socketFactory ?? SecureSocketFactory(); } final _logger = AtSignLogger('SecondaryUrlFinder'); @@ -188,11 +189,11 @@ class SecondaryUrlFinder { var prompt = false; var once = true; - socket = await _socketFactory.createSocket( + socket = await _socketFactory.create( _rootDomain, '$_rootPort', SecureSocketConfig()); _logger.finer('findSecondaryUrl: connection to root server established'); // listen to the received data event stream - socket.listen((List event) async { + socket!.listen((List event) async { _logger.finest('root socket listener received: $event'); answer = utf8.decode(event); diff --git a/packages/at_lookup/lib/src/connection/at_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_connection_factory.dart new file mode 100644 index 00000000..f1ae8184 --- /dev/null +++ b/packages/at_lookup/lib/src/connection/at_connection_factory.dart @@ -0,0 +1,79 @@ +import 'dart:io'; + +import 'package:at_commons/at_commons.dart'; +import 'package:at_lookup/at_lookup.dart'; + +import 'outbound_message_listener.dart'; + + +abstract class AtConnectionFactory { + Future create(String host, String port, SecureSocketConfig secureSocketConfig); + + U outBoundConnectionFactory(T socket); + + OutboundMessageListener listenerFactory(U connection); + + // New method to indicate the connection type + String get connectionType; +} + +class SecureSocketFactory extends AtConnectionFactory { + @override + Future create( + String host, String port, SecureSocketConfig secureSocketConfig) async { + return await SecureSocketUtil.createSecureSocket( + host, port, secureSocketConfig); + } + + @override + OutboundConnection outBoundConnectionFactory(SecureSocket socket) { + return OutboundConnectionImpl(socket); + } + + @override + OutboundMessageListener listenerFactory(OutboundConnection connection) { + return OutboundMessageListener(connection); + } + + @override + String get connectionType => 'SecureSocket'; +} + +class WebSocketFactory extends AtConnectionFactory { + @override + Future create( + String host, String port, SecureSocketConfig secureSocketConfig) async { + final socket = await SecureSocketUtil.createSecureSocket( + host, port, secureSocketConfig, + isWebSocket: true); + return socket as WebSocket; + } + + @override + OutboundWebSocketConnection outBoundConnectionFactory(WebSocket socket) { + return OutboundWebsocketConnectionImpl(socket); + } + + @override + OutboundMessageListener listenerFactory(OutboundWebSocketConnection connection) { + return OutboundMessageListener(connection); + } + + @override + String get connectionType => 'WebSocket'; +} + + + +// abstract class AtConnectionFactory { +// Future createSocket( +// String host, String port, SecureSocketConfig socketConfig); +// Future createWebSocket( +// String host, String port, SecureSocketConfig socketConfig); +// OutboundMessageListener createListener(OutboundConnection outboundConnection); +// OutboundMessageListener createWebSocketListener( +// OutboundWebSocketConnection outboundWebSocketConnection); +// OutboundConnection createOutboundConnection(SecureSocket secureSocket); +// OutboundWebSocketConnection createWebSocketOutboundConnection( +// WebSocket webSocket); +// } diff --git a/packages/at_lookup/lib/src/util/secure_socket_util.dart b/packages/at_lookup/lib/src/util/secure_socket_util.dart index d4f853ff..a9982e5e 100644 --- a/packages/at_lookup/lib/src/util/secure_socket_util.dart +++ b/packages/at_lookup/lib/src/util/secure_socket_util.dart @@ -11,13 +11,13 @@ class SecureSocketUtil { String host, String port, SecureSocketConfig secureSocketConfig, {bool isWebSocket = false}) async { if (isWebSocket) { - return createSecureWebSocket(host, port, secureSocketConfig); + return _createSecureWebSocket(host, port, secureSocketConfig); } else { return _createSecureSocket(host, port, secureSocketConfig); } } - static Future createSecureWebSocket( + static Future _createSecureWebSocket( String host, String port, SecureSocketConfig secureSocketConfig) async { try { Random r = Random(); diff --git a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart new file mode 100644 index 00000000..27612487 --- /dev/null +++ b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart @@ -0,0 +1,96 @@ +import 'package:at_commons/at_builders.dart'; +import 'package:at_commons/at_commons.dart'; +import 'package:at_demo_data/at_demo_data.dart' as at_demos; +import 'package:at_lookup/at_lookup.dart'; +import 'package:at_chops/at_chops.dart'; + +import 'package:test/test.dart'; + +void main() { + String atSign = '@bob'; + AtChops atChopsKeys = createAtChopsFromDemoKeys(atSign); + + group('A group of tests to assert on authenticate functionality', () { + test( + 'A test to verify a websocket connection and do a cram authenticate and scan', + () async { + var atLookup = + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); + var command = 'scan\n'; + var response = await atLookup.executeCommand(command, auth: true); + expect(response, contains('public:signing_publickey$atSign')); + }); + + test( + 'A test to verify a websocket connection and do a cram authenticate and update', + () async { + var atLookup = + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); + // update public and private keys manually + var command = + 'update:privatekey:at_pkam_publickey ${at_demos.pkamPublicKeyMap[atSign]}\n'; + var response = await atLookup.executeCommand(command, auth: true); + expect(response, 'data:-1'); + command = + 'update:public:publickey${atSign} ${at_demos.encryptionPublicKeyMap[atSign]}\n'; + await atLookup.executeCommand(command, auth: true); + print(response); + assert((!response!.contains('Invalid syntax')) && + (!response.contains('null'))); + }); + + test( + 'A test to verify a websocket connection and do a pkam authenticate and executeCommand', + () async { + var atLookup = + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + atLookup.atChops = atChopsKeys; + await atLookup.pkamAuthenticate(); + var command = 'update:public:username$atSign bob123\n'; + var response = await atLookup.executeCommand(command, auth: true); + assert((!response!.contains('Invalid syntax')) && + (!response.contains('null'))); + }); + + test( + 'A test to verify a websocket connection and do a pkam authenticate and execute verb', + () async { + var atLookup = + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + atLookup.atChops = atChopsKeys; + await atLookup.pkamAuthenticate(); + var atKey = 'key1'; + String value = 'value1'; + var updateBuilder = UpdateVerbBuilder() + ..value = 'value1' + ..atKey = (AtKey() + ..key = atKey + ..sharedBy = atSign + ..metadata = (Metadata()..isPublic = true)); + var response = await atLookup.executeVerb(updateBuilder); + print(response); + assert((!response.contains('Invalid syntax')) && + (!response.contains('null'))); + var llookupVerbBuilder = LLookupVerbBuilder() + ..atKey = (AtKey() + ..key = atKey + ..sharedBy = atSign + ..metadata = (Metadata()..isPublic = true)); + response = await atLookup.executeVerb(llookupVerbBuilder); + expect(response, contains(value)); + }, timeout: Timeout(Duration(minutes: 5))); + }); +} + +AtChops createAtChopsFromDemoKeys(String atSign) { + var atEncryptionKeyPair = AtEncryptionKeyPair.create( + at_demos.encryptionPublicKeyMap[atSign]!, + at_demos.encryptionPrivateKeyMap[atSign]!); + var atPkamKeyPair = AtPkamKeyPair.create( + at_demos.pkamPublicKeyMap[atSign]!, at_demos.pkamPrivateKeyMap[atSign]!); + final atChopsKeys = AtChopsKeys.create(atEncryptionKeyPair, atPkamKeyPair); + atChopsKeys.selfEncryptionKey = AESKey(at_demos.aesKeyMap[atSign]!); + return AtChopsImpl(atChopsKeys); +} From 3f453afd02308a8dfc3e0bc5d7e3af1a8a207fed Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Tue, 5 Nov 2024 03:51:52 +0530 Subject: [PATCH 07/23] removed commented code --- .../src/connection/at_connection_factory.dart | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/at_lookup/lib/src/connection/at_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_connection_factory.dart index f1ae8184..a0bcdb57 100644 --- a/packages/at_lookup/lib/src/connection/at_connection_factory.dart +++ b/packages/at_lookup/lib/src/connection/at_connection_factory.dart @@ -61,19 +61,4 @@ class WebSocketFactory extends AtConnectionFactory 'WebSocket'; -} - - - -// abstract class AtConnectionFactory { -// Future createSocket( -// String host, String port, SecureSocketConfig socketConfig); -// Future createWebSocket( -// String host, String port, SecureSocketConfig socketConfig); -// OutboundMessageListener createListener(OutboundConnection outboundConnection); -// OutboundMessageListener createWebSocketListener( -// OutboundWebSocketConnection outboundWebSocketConnection); -// OutboundConnection createOutboundConnection(SecureSocket secureSocket); -// OutboundWebSocketConnection createWebSocketOutboundConnection( -// WebSocket webSocket); -// } +} \ No newline at end of file From a72cf1ac940bb85731998bea57efc5142e549118 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Wed, 6 Nov 2024 02:35:43 +0530 Subject: [PATCH 08/23] addressed review comments --- packages/at_lookup/lib/at_lookup.dart | 3 +- .../at_lookup/lib/src/at_lookup_impl.dart | 18 ++-- .../cacheable_secondary_address_finder.dart | 9 +- .../src/connection/at_connection_factory.dart | 64 ------------- ...at_lookup_outbound_connection_factory.dart | 91 +++++++++++++++++++ 5 files changed, 106 insertions(+), 79 deletions(-) delete mode 100644 packages/at_lookup/lib/src/connection/at_connection_factory.dart create mode 100644 packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart diff --git a/packages/at_lookup/lib/at_lookup.dart b/packages/at_lookup/lib/at_lookup.dart index 0d37d816..d8d992ba 100644 --- a/packages/at_lookup/lib/at_lookup.dart +++ b/packages/at_lookup/lib/at_lookup.dart @@ -13,4 +13,5 @@ export 'src/monitor_client.dart'; export 'src/cache/secondary_address_finder.dart'; export 'src/cache/cacheable_secondary_address_finder.dart'; export 'src/util/secure_socket_util.dart'; -export 'src/connection/outbound_websocket_connection_impl.dart'; \ No newline at end of file +export 'src/connection/outbound_websocket_connection_impl.dart'; +export 'src/connection/at_lookup_outbound_connection_factory.dart'; \ No newline at end of file diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 09ccd8a0..252fb602 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -16,7 +16,6 @@ import 'package:crypto/crypto.dart'; import 'package:crypton/crypton.dart'; import 'package:mutex/mutex.dart'; -import 'connection/at_connection_factory.dart'; class AtLookupImpl implements AtLookUp { final logger = AtSignLogger('AtLookup'); @@ -32,7 +31,7 @@ class AtLookupImpl implements AtLookUp { OutboundWebSocketConnection? get webSocketConnection => _webSocketConnection; - late AtConnectionFactory atConnectionFactory; + late AtLookupOutboundConnectionFactory atSocketFactory; @override late SecondaryAddressFinder secondaryAddressFinder; @@ -67,9 +66,9 @@ class AtLookupImpl implements AtLookUp { bool useWebSocket = false}) // Add a flag for WebSocket or SecureSocket { // Determine the factory type based on the useWebSocket flag - atConnectionFactory = useWebSocket - ? WebSocketFactory() - : SecureSocketFactory() as AtConnectionFactory; + atSocketFactory = useWebSocket + ? AtLookupWebSocketFactory() + : AtLookupSecureSocketFactory() as AtLookupOutboundConnectionFactory; _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; @@ -649,13 +648,14 @@ class AtLookupImpl implements AtLookUp { String host, String port, SecureSocketConfig secureSocketConfig) async { try { // Create the socket connection using the factory - final connection = - await atConnectionFactory.create(host, port, secureSocketConfig); + final connection = await atSocketFactory.createUnderlying( + host, port, secureSocketConfig); // Create the outbound connection and listener using the factory's methods final outboundConnection = - atConnectionFactory.outBoundConnectionFactory(connection); - messageListener = atConnectionFactory.listenerFactory(outboundConnection); + atSocketFactory.outBoundConnectionFactory(connection); + messageListener = + atSocketFactory.atLookupSocketListenerFactory(outboundConnection); // Set the connection type in `_webSocketConnection` or `_connection` if (connection is WebSocket) { diff --git a/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart b/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart index 574288e3..7bc97ca9 100644 --- a/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart +++ b/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; -import 'package:at_lookup/src/connection/at_connection_factory.dart'; import 'package:at_lookup/src/util/lookup_util.dart'; import 'package:at_utils/at_logger.dart'; @@ -129,11 +128,11 @@ class SecondaryAddressCacheEntry { class SecondaryUrlFinder { final String _rootDomain; final int _rootPort; - late final AtConnectionFactory _socketFactory; + late final AtLookupOutboundConnectionFactory _socketFactory; SecondaryUrlFinder(this._rootDomain, this._rootPort, - {AtConnectionFactory? socketFactory}) { - _socketFactory = socketFactory ?? SecureSocketFactory(); + {AtLookupOutboundConnectionFactory? socketFactory}) { + _socketFactory = socketFactory ?? AtLookupSecureSocketFactory(); } final _logger = AtSignLogger('SecondaryUrlFinder'); @@ -189,7 +188,7 @@ class SecondaryUrlFinder { var prompt = false; var once = true; - socket = await _socketFactory.create( + socket = await _socketFactory.createUnderlying( _rootDomain, '$_rootPort', SecureSocketConfig()); _logger.finer('findSecondaryUrl: connection to root server established'); // listen to the received data event stream diff --git a/packages/at_lookup/lib/src/connection/at_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_connection_factory.dart deleted file mode 100644 index a0bcdb57..00000000 --- a/packages/at_lookup/lib/src/connection/at_connection_factory.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'dart:io'; - -import 'package:at_commons/at_commons.dart'; -import 'package:at_lookup/at_lookup.dart'; - -import 'outbound_message_listener.dart'; - - -abstract class AtConnectionFactory { - Future create(String host, String port, SecureSocketConfig secureSocketConfig); - - U outBoundConnectionFactory(T socket); - - OutboundMessageListener listenerFactory(U connection); - - // New method to indicate the connection type - String get connectionType; -} - -class SecureSocketFactory extends AtConnectionFactory { - @override - Future create( - String host, String port, SecureSocketConfig secureSocketConfig) async { - return await SecureSocketUtil.createSecureSocket( - host, port, secureSocketConfig); - } - - @override - OutboundConnection outBoundConnectionFactory(SecureSocket socket) { - return OutboundConnectionImpl(socket); - } - - @override - OutboundMessageListener listenerFactory(OutboundConnection connection) { - return OutboundMessageListener(connection); - } - - @override - String get connectionType => 'SecureSocket'; -} - -class WebSocketFactory extends AtConnectionFactory { - @override - Future create( - String host, String port, SecureSocketConfig secureSocketConfig) async { - final socket = await SecureSocketUtil.createSecureSocket( - host, port, secureSocketConfig, - isWebSocket: true); - return socket as WebSocket; - } - - @override - OutboundWebSocketConnection outBoundConnectionFactory(WebSocket socket) { - return OutboundWebsocketConnectionImpl(socket); - } - - @override - OutboundMessageListener listenerFactory(OutboundWebSocketConnection connection) { - return OutboundMessageListener(connection); - } - - @override - String get connectionType => 'WebSocket'; -} \ No newline at end of file diff --git a/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart new file mode 100644 index 00000000..0448cfb4 --- /dev/null +++ b/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart @@ -0,0 +1,91 @@ +import 'dart:io'; + +import 'package:at_commons/at_commons.dart'; +import 'package:at_lookup/at_lookup.dart'; + +import 'outbound_message_listener.dart'; + +/// This factory is responsible for creating the underlying connection, +/// an outbound connection wrapper, and the message listener for a +/// specific type of connection (e.g., `SecureSocket` or `WebSocket`). +abstract class AtLookupOutboundConnectionFactory { + /// Creates the underlying connection of type [T]. + /// + /// Takes [host], [port], and [secureSocketConfig] as parameters to establish + /// a secure connection based on the provided configuration. + Future createUnderlying( + String host, String port, SecureSocketConfig secureSocketConfig); + + /// Wraps the underlying connection of type [T] into an outbound connection [U]. + U outBoundConnectionFactory(T underlying); + + /// Creates an [OutboundMessageListener] to manage messages for the given [U] connection. + OutboundMessageListener atLookupSocketListenerFactory(U connection); +} + +/// Factory class to create a secure outbound connection over [SecureSocket]. +/// +/// This class handles the creation of a secure socket-based connection, +/// its outbound connection wrapper, and the corresponding message listener. +class AtLookupSecureSocketFactory extends AtLookupOutboundConnectionFactory< + SecureSocket, OutboundConnection> { + /// Creates a secure socket connection to the specified [host] and [port] + /// using the given [secureSocketConfig]. + /// + /// Returns a [SecureSocket] that establishes a secure connection + @override + Future createUnderlying( + String host, String port, SecureSocketConfig secureSocketConfig) async { + return await SecureSocketUtil.createSecureSocket( + host, port, secureSocketConfig); + } + + /// Wraps the [SecureSocket] connection into an [OutboundConnection] instance. + @override + OutboundConnection outBoundConnectionFactory(SecureSocket underlying) { + return OutboundConnectionImpl(underlying); + } + + /// Creates an [OutboundMessageListener] to manage messages for the secure + /// socket-based [OutboundConnection]. + @override + OutboundMessageListener atLookupSocketListenerFactory( + OutboundConnection connection) { + return OutboundMessageListener(connection); + } +} + +/// Factory class to create a WebSocket-based outbound connection. +/// +/// This class handles the creation of a WebSocket connection, +/// its outbound connection wrapper, and the associated message listener. +class AtLookupWebSocketFactory extends AtLookupOutboundConnectionFactory< + WebSocket, OutboundWebSocketConnection> { + /// Creates a WebSocket connection to the specified [host] and [port] + /// using the given [secureSocketConfig]. + @override + Future createUnderlying( + String host, String port, SecureSocketConfig secureSocketConfig) async { + final socket = await SecureSocketUtil.createSecureSocket( + host, port, secureSocketConfig, + isWebSocket: true); + return socket as WebSocket; + } + + /// Wraps the [WebSocket] connection into an [OutboundWebSocketConnection] instance. + /// + /// This outbound connection manages WebSocket-specific message handling and + /// provides additional methods for WebSocket communication. + @override + OutboundWebSocketConnection outBoundConnectionFactory(WebSocket underlying) { + return OutboundWebsocketConnectionImpl(underlying); + } + + /// Creates an [OutboundMessageListener] to manage messages for the + /// WebSocket-based [OutboundWebSocketConnection]. + @override + OutboundMessageListener atLookupSocketListenerFactory( + OutboundWebSocketConnection connection) { + return OutboundMessageListener(connection); + } +} From a7c8e644d3c40a47324ec3aa92c3a68beeb2dbb6 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Wed, 13 Nov 2024 03:48:15 +0530 Subject: [PATCH 09/23] updated unit tests --- packages/at_lookup/test/at_lookup_test.dart | 128 +++++++----------- .../at_lookup/test/at_lookup_test_utils.dart | 10 +- .../test/connection_management_test.dart | 60 ++++++-- .../test/secondary_address_cache_test.dart | 6 +- 4 files changed, 100 insertions(+), 104 deletions(-) diff --git a/packages/at_lookup/test/at_lookup_test.dart b/packages/at_lookup/test/at_lookup_test.dart index c248089f..38015029 100644 --- a/packages/at_lookup/test/at_lookup_test.dart +++ b/packages/at_lookup/test/at_lookup_test.dart @@ -20,9 +20,7 @@ void main() { late OutboundConnection mockOutBoundConnection; late SecondaryAddressFinder mockSecondaryAddressFinder; late OutboundMessageListener mockOutboundListener; - late AtLookupSecureSocketFactory mockSocketFactory; - late AtLookupSecureSocketListenerFactory mockSecureSocketListenerFactory; - late AtLookupOutboundConnectionFactory mockOutboundConnectionFactory; + late AtLookupOutboundConnectionFactory mockAtConnectionFactory; late AtChops mockAtChops; late SecureSocket mockSecureSocket; @@ -34,9 +32,7 @@ void main() { mockOutBoundConnection = MockOutboundConnectionImpl(); mockSecondaryAddressFinder = MockSecondaryAddressFinder(); mockOutboundListener = MockOutboundMessageListener(); - mockSocketFactory = MockSecureSocketFactory(); - mockSecureSocketListenerFactory = MockSecureSocketListenerFactory(); - mockOutboundConnectionFactory = MockOutboundConnectionFactory(); + mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); mockAtChops = MockAtChops(); registerFallbackValue(SecureSocketConfig()); mockSecureSocket = createMockAtServerSocket(atServerHost, atServerPort); @@ -45,25 +41,21 @@ void main() { .thenAnswer((_) async { return SecondaryAddress(atServerHost, atServerPort); }); - when(() => mockSocketFactory.createSocket(atServerHost, '12345', any())) - .thenAnswer((invocation) { + + when(() => mockAtConnectionFactory.createUnderlying( + atServerHost, '12345', any())).thenAnswer((_) { return Future.value(mockSecureSocket); }); - when(() => mockOutboundConnectionFactory - .createOutboundConnection(mockSecureSocket)).thenAnswer((invocation) { - print('Creating mock outbound connection'); - return mockOutBoundConnection; - }); - when(() => mockSecureSocketListenerFactory - .createListener(mockOutBoundConnection)).thenAnswer((invocation) { - print('creating mock outbound listener'); - return mockOutboundListener; - }); - when(() => mockOutBoundConnection.write('from:@alice\n')) - .thenAnswer((invocation) { - mockSecureSocket.write('from:@alice\n'); - return Future.value(); - }); + + when(() => + mockAtConnectionFactory.outBoundConnectionFactory(mockSecureSocket)) + .thenAnswer((_) => mockOutBoundConnection); + + when(() => mockOutBoundConnection.write(any())) + .thenAnswer((_) => Future.value()); + + when(() => mockAtConnectionFactory.atLookupSocketListenerFactory( + mockOutBoundConnection)).thenAnswer((_) => mockOutboundListener); }); group('A group of tests to verify atlookup pkam authentication', () { @@ -93,11 +85,14 @@ void main() { return Future.value(); }); - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + final atLookup = AtLookupImpl( + '@alice', + atServerHost, + 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + ); + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate(); expect(result, true); @@ -130,10 +125,9 @@ void main() { }); final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect(() async => await atLookup.pkamAuthenticate(), throwsA(predicate((e) => e is UnAuthenticatedException))); @@ -166,10 +160,8 @@ void main() { }); final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate(enrollmentId: enrollmentIdFromServer); @@ -193,7 +185,6 @@ void main() { when(() => mockOutBoundConnection.getMetaData()) .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - when(() => mockOutBoundConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) .thenAnswer((invocation) { @@ -203,10 +194,8 @@ void main() { }); final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect( () async => await atLookup.pkamAuthenticate( @@ -219,25 +208,20 @@ void main() { group('A group of tests to verify executeCommand method', () { test('executeCommand - from verb - auth false', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; final fromResponse = 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; when(() => mockOutboundListener.read()) .thenAnswer((_) => Future.value(fromResponse)); var result = await atLookup.executeCommand('from:@alice\n'); expect(result, fromResponse); - }); + }, timeout: Timeout(Duration(minutes: 5))); test('executeCommand -llookup verb - auth true - auth key not set', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); final fromResponse = 'data:1234'; when(() => mockOutboundListener.read()) .thenAnswer((_) => Future.value(fromResponse)); @@ -249,10 +233,8 @@ void main() { test('executeCommand -llookup verb - auth true - at_chops set', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'data:1234'; @@ -269,10 +251,8 @@ void main() { test('executeCommand - test non json error handling', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'error:AT0015-Exception: fubar'; @@ -291,10 +271,8 @@ void main() { test('executeCommand - test json error handling', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = @@ -316,11 +294,8 @@ void main() { group('Validate executeVerb() behaviour', () { test('validate EnrollVerbHandler behaviour - request', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); - + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; String appName = 'unit_test_1'; String deviceName = 'test_device'; String otp = 'ABCDEF'; @@ -354,12 +329,9 @@ void main() { test('validate behaviour with EnrollVerbHandler - approve', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); atLookup.atChops = mockAtChops; - + atLookup.atSocketFactory = mockAtConnectionFactory; String appName = 'unit_test_2'; String deviceName = 'test_device'; String enrollmentId = '1357913579'; @@ -392,11 +364,9 @@ void main() { test('validate behaviour with EnrollVerbHandler - revoke', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); atLookup.atChops = mockAtChops; + atLookup.atSocketFactory = mockAtConnectionFactory; String enrollmentId = '89213647826348'; EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() @@ -424,10 +394,8 @@ void main() { test('validate behaviour with EnrollVerbHandler - deny', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - secureSocketFactory: mockSocketFactory, - socketListenerFactory: mockSecureSocketListenerFactory, - outboundConnectionFactory: mockOutboundConnectionFactory); + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; String enrollmentId = '5754765754'; diff --git a/packages/at_lookup/test/at_lookup_test_utils.dart b/packages/at_lookup/test/at_lookup_test_utils.dart index 16f16287..3ed78fcd 100644 --- a/packages/at_lookup/test/at_lookup_test_utils.dart +++ b/packages/at_lookup/test/at_lookup_test_utils.dart @@ -13,8 +13,8 @@ class MockSecondaryAddressFinder extends Mock class MockSecondaryUrlFinder extends Mock implements SecondaryUrlFinder {} -class MockSecureSocketFactory extends Mock - implements AtLookupSecureSocketFactory {} +class MockAtLookupOutboundConnectionFactory extends Mock + implements AtLookupOutboundConnectionFactory {} class MockStreamSubscription extends Mock implements StreamSubscription {} @@ -23,12 +23,6 @@ class MockSecureSocket extends Mock implements SecureSocket { int mockNumber = mockSocketNumber++; } -class MockSecureSocketListenerFactory extends Mock - implements AtLookupSecureSocketListenerFactory {} - -class MockOutboundConnectionFactory extends Mock - implements AtLookupOutboundConnectionFactory {} - class MockOutboundMessageListener extends Mock implements OutboundMessageListener {} diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index 1105aba1..b1463fdd 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -4,8 +4,9 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; -import 'package:test/test.dart'; +import 'package:at_utils/at_logger.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; import 'at_lookup_test_utils.dart'; @@ -13,9 +14,31 @@ class MockOutboundConnectionImpl extends Mock implements OutboundConnectionImpl {} void main() { + AtSignLogger.root_level = 'finest'; group('test connection close and socket cleanup', () { late SecondaryAddressFinder finder; - late MockSecureSocketFactory mockSocketFactory; + late AtLookupOutboundConnectionFactory mockAtConnectionFactory; + late OutboundMessageListener mockOutboundListener; + late OutboundConnection mockOutBoundConnection; + + + // setUp(() { + // mockSocketNumber = 1; + + // finder = MockSecondaryAddressFinder(); + // when(() => finder.findSecondary(any())).thenAnswer((invocation) => + // Future.value( + // SecondaryAddress('test.test.test', 12345))); + + // mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); + // registerFallbackValue(SecureSocketConfig()); + // when(() => + // mockAtConnectionFactory.createUnderlying('test.test.test', '12345', any())) + // .thenAnswer((invocation) { + // return Future.value( + // createMockAtServerSocket('test.test.test', 12345)); + // }); + // }); setUp(() { mockSocketNumber = 1; @@ -25,23 +48,32 @@ void main() { Future.value( SecondaryAddress('test.test.test', 12345))); - mockSocketFactory = MockSecureSocketFactory(); + mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); + mockOutBoundConnection = MockOutboundConnectionImpl(); + mockOutboundListener = MockOutboundMessageListener(); registerFallbackValue(SecureSocketConfig()); - when(() => - mockSocketFactory.createSocket('test.test.test', '12345', any())) - .thenAnswer((invocation) { + when(() => mockAtConnectionFactory.createUnderlying( + 'test.test.test', '12345', any())).thenAnswer((invocation) { return Future.value( createMockAtServerSocket('test.test.test', 12345)); }); + when(() => mockAtConnectionFactory.outBoundConnectionFactory(any())) + .thenAnswer((_) => mockOutBoundConnection); + + when(() => mockOutBoundConnection.write(any())) + .thenAnswer((_) => Future.value()); + + when(() => mockAtConnectionFactory.atLookupSocketListenerFactory( + mockOutBoundConnection)).thenAnswer((_) => mockOutboundListener); }); test( 'test AtLookupImpl will use its default SecureSocketFactory if none is provided to it', () async { AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, - secondaryAddressFinder: finder, secureSocketFactory: null); + secondaryAddressFinder: finder); - expect(atLookup.socketFactory.runtimeType.toString(), + expect(atLookup.atSocketFactory.runtimeType.toString(), "AtLookupSecureSocketFactory"); expect(() async => await atLookup.createConnection(), throwsA(predicate((dynamic e) => e is SecondaryConnectException))); @@ -51,10 +83,12 @@ void main() { 'test AtLookupImpl closes invalid connections before creating new ones', () async { AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, - secondaryAddressFinder: finder, - secureSocketFactory: mockSocketFactory); - expect(atLookup.socketFactory.runtimeType.toString(), - "MockSecureSocketFactory"); + secondaryAddressFinder: finder); + expect(atLookup.atSocketFactory.runtimeType.toString(), + "AtLookupSecureSocketFactory"); + + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; await atLookup.createConnection(); @@ -91,7 +125,7 @@ void main() { expect(secondSocket.destroyed, false); expect(secondConnection.metaData!.isClosed, false); expect(secondConnection.isInValid(), false); - }); + }, timeout: Timeout(Duration(minutes: 5))); test( 'test message listener closes connection' diff --git a/packages/at_lookup/test/secondary_address_cache_test.dart b/packages/at_lookup/test/secondary_address_cache_test.dart index 326220f3..d6e1e0db 100644 --- a/packages/at_lookup/test/secondary_address_cache_test.dart +++ b/packages/at_lookup/test/secondary_address_cache_test.dart @@ -141,7 +141,7 @@ void main() async { late Function socketOnDataFn; late SecureSocket mockSocket; - late MockSecureSocketFactory mockSocketFactory; + late MockAtLookupOutboundConnectionFactory mockSocketFactory; late CacheableSecondaryAddressFinder cachingAtServerFinder; @@ -162,7 +162,7 @@ void main() async { setUp(() { mockSocket = createMockAtDirectorySocket(mockAtDirectoryHost, 64); - mockSocketFactory = MockSecureSocketFactory(); + mockSocketFactory = MockAtLookupOutboundConnectionFactory(); cachingAtServerFinder = CacheableSecondaryAddressFinder( mockAtDirectoryHost, 64, @@ -171,7 +171,7 @@ void main() async { numSocketCreateCalls = 0; when(() => - mockSocketFactory.createSocket(mockAtDirectoryHost, '64', any())) + mockSocketFactory.createUnderlying(mockAtDirectoryHost, '64', any())) .thenAnswer((invocation) { print( 'mock create socket: numFailures $numSocketCreateCalls requiredFailures $requiredFailures'); From aee94bbd217b6a88c9bfb6fd2600adc459fed05c Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Wed, 13 Nov 2024 03:52:32 +0530 Subject: [PATCH 10/23] fixed failing test --- .../test/connection_management_test.dart | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index b1463fdd..8033ce77 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -17,54 +17,29 @@ void main() { AtSignLogger.root_level = 'finest'; group('test connection close and socket cleanup', () { late SecondaryAddressFinder finder; - late AtLookupOutboundConnectionFactory mockAtConnectionFactory; + late MockAtLookupOutboundConnectionFactory mockSecureSocketFactory; + late SecureSocket mockSecureSocket; late OutboundMessageListener mockOutboundListener; - late OutboundConnection mockOutBoundConnection; - - - // setUp(() { - // mockSocketNumber = 1; - - // finder = MockSecondaryAddressFinder(); - // when(() => finder.findSecondary(any())).thenAnswer((invocation) => - // Future.value( - // SecondaryAddress('test.test.test', 12345))); - - // mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); - // registerFallbackValue(SecureSocketConfig()); - // when(() => - // mockAtConnectionFactory.createUnderlying('test.test.test', '12345', any())) - // .thenAnswer((invocation) { - // return Future.value( - // createMockAtServerSocket('test.test.test', 12345)); - // }); - // }); + late MockOutboundConnectionImpl mockOutboundConnection; setUp(() { - mockSocketNumber = 1; - finder = MockSecondaryAddressFinder(); - when(() => finder.findSecondary(any())).thenAnswer((invocation) => - Future.value( - SecondaryAddress('test.test.test', 12345))); - - mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); - mockOutBoundConnection = MockOutboundConnectionImpl(); mockOutboundListener = MockOutboundMessageListener(); + // Mock outbound connection creation for the first socket + mockOutboundConnection = MockOutboundConnectionImpl(); + mockSecureSocket = createMockAtServerSocket('test.test.test', 12345); + when(() => finder.findSecondary(any())) + .thenAnswer((_) async => SecondaryAddress('test.test.test', 12345)); + + mockSecureSocketFactory = MockAtLookupOutboundConnectionFactory(); registerFallbackValue(SecureSocketConfig()); - when(() => mockAtConnectionFactory.createUnderlying( + + mockSocketNumber = 1; + when(() => mockSecureSocketFactory.createUnderlying( 'test.test.test', '12345', any())).thenAnswer((invocation) { return Future.value( createMockAtServerSocket('test.test.test', 12345)); }); - when(() => mockAtConnectionFactory.outBoundConnectionFactory(any())) - .thenAnswer((_) => mockOutBoundConnection); - - when(() => mockOutBoundConnection.write(any())) - .thenAnswer((_) => Future.value()); - - when(() => mockAtConnectionFactory.atLookupSocketListenerFactory( - mockOutBoundConnection)).thenAnswer((_) => mockOutboundListener); }); test( @@ -87,8 +62,14 @@ void main() { expect(atLookup.atSocketFactory.runtimeType.toString(), "AtLookupSecureSocketFactory"); - // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockSecureSocketFactory; + + when(() => mockSecureSocketFactory.outBoundConnectionFactory( + mockSecureSocket)).thenReturn(mockOutboundConnection); + + when(() => mockSecureSocketFactory.atLookupSocketListenerFactory( + mockOutboundConnection)).thenAnswer((_) => mockOutboundListener); await atLookup.createConnection(); From a67171bcbe9f63b6562c0d0a6c9a4c2fa7bc5625 Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Wed, 13 Nov 2024 09:20:14 +0530 Subject: [PATCH 11/23] fix: Updated mock references to prevent null pointer errors --- .../test/connection_management_test.dart | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index 8033ce77..e237f943 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; import 'package:at_utils/at_logger.dart'; import 'package:mocktail/mocktail.dart'; @@ -17,7 +18,7 @@ void main() { AtSignLogger.root_level = 'finest'; group('test connection close and socket cleanup', () { late SecondaryAddressFinder finder; - late MockAtLookupOutboundConnectionFactory mockSecureSocketFactory; + late MockAtLookupOutboundConnectionFactory mockOutboundConnectionFactory; late SecureSocket mockSecureSocket; late OutboundMessageListener mockOutboundListener; late MockOutboundConnectionImpl mockOutboundConnection; @@ -31,14 +32,13 @@ void main() { when(() => finder.findSecondary(any())) .thenAnswer((_) async => SecondaryAddress('test.test.test', 12345)); - mockSecureSocketFactory = MockAtLookupOutboundConnectionFactory(); + mockOutboundConnectionFactory = MockAtLookupOutboundConnectionFactory(); registerFallbackValue(SecureSocketConfig()); mockSocketNumber = 1; - when(() => mockSecureSocketFactory.createUnderlying( + when(() => mockOutboundConnectionFactory.createUnderlying( 'test.test.test', '12345', any())).thenAnswer((invocation) { - return Future.value( - createMockAtServerSocket('test.test.test', 12345)); + return Future.value(mockSecureSocket); }); }); @@ -63,14 +63,25 @@ void main() { "AtLookupSecureSocketFactory"); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockSecureSocketFactory; + atLookup.atSocketFactory = mockOutboundConnectionFactory; - when(() => mockSecureSocketFactory.outBoundConnectionFactory( + when(() => mockOutboundConnection.underlying) + .thenAnswer((_) => mockSecureSocket); + + when(() => mockOutboundConnectionFactory.outBoundConnectionFactory( mockSecureSocket)).thenReturn(mockOutboundConnection); - when(() => mockSecureSocketFactory.atLookupSocketListenerFactory( + when(() => mockOutboundConnectionFactory.atLookupSocketListenerFactory( mockOutboundConnection)).thenAnswer((_) => mockOutboundListener); + AtConnectionMetaData outboundConnectionMetadata = + OutboundConnectionMetadata(); + + when(() => mockOutboundConnection.metaData) + .thenAnswer((_) => outboundConnectionMetadata); + when(() => mockOutboundConnection.isInValid()) + .thenAnswer((_) => (mockSecureSocket as MockSecureSocket).destroyed); + await atLookup.createConnection(); // let's get a handle to the first socket & connection From 49b840b7ccb914fcf61f06539d8857baedfbf4b3 Mon Sep 17 00:00:00 2001 From: Sitaram Kalluri Date: Wed, 13 Nov 2024 21:34:45 +0530 Subject: [PATCH 12/23] fix: Add unit test - A test to verify the connections are invalidated after the connection time-outs. --- .../test/connection_management_test.dart | 183 ++++++++++++------ 1 file changed, 120 insertions(+), 63 deletions(-) diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index e237f943..35dd5655 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; -import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; import 'package:at_utils/at_logger.dart'; import 'package:mocktail/mocktail.dart'; @@ -14,6 +13,9 @@ import 'at_lookup_test_utils.dart'; class MockOutboundConnectionImpl extends Mock implements OutboundConnectionImpl {} +class MockAtLookupSecureSocketFactory extends Mock + implements AtLookupSecureSocketFactory {} + void main() { AtSignLogger.root_level = 'finest'; group('test connection close and socket cleanup', () { @@ -55,69 +57,124 @@ void main() { }); test( - 'test AtLookupImpl closes invalid connections before creating new ones', + 'A test to verify the connections are invalidated after the connection time-outs', () async { - AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, - secondaryAddressFinder: finder); - expect(atLookup.atSocketFactory.runtimeType.toString(), - "AtLookupSecureSocketFactory"); - - // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockOutboundConnectionFactory; - - when(() => mockOutboundConnection.underlying) - .thenAnswer((_) => mockSecureSocket); - - when(() => mockOutboundConnectionFactory.outBoundConnectionFactory( - mockSecureSocket)).thenReturn(mockOutboundConnection); - - when(() => mockOutboundConnectionFactory.atLookupSocketListenerFactory( - mockOutboundConnection)).thenAnswer((_) => mockOutboundListener); - - AtConnectionMetaData outboundConnectionMetadata = - OutboundConnectionMetadata(); - - when(() => mockOutboundConnection.metaData) - .thenAnswer((_) => outboundConnectionMetadata); - when(() => mockOutboundConnection.isInValid()) - .thenAnswer((_) => (mockSecureSocket as MockSecureSocket).destroyed); - - await atLookup.createConnection(); - - // let's get a handle to the first socket & connection - OutboundConnection firstConnection = atLookup.connection!; - MockSecureSocket firstSocket = - firstConnection.underlying as MockSecureSocket; - - expect(firstSocket.mockNumber, 1); - expect(firstSocket.destroyed, false); - expect(firstConnection.metaData!.isClosed, false); - expect(firstConnection.isInValid(), false); - - // Make the connection appear 'idle' - firstConnection.setIdleTime(1); - await Future.delayed(Duration(milliseconds: 2)); - expect(firstConnection.isInValid(), true); - - // When we now call AtLookupImpl's createConnection again, it should: - // - notice that its current connection is 'idle', and close it - // - create a new connection - await atLookup.createConnection(); - - // has the first connection been closed, and its socket destroyed? - expect(firstSocket.destroyed, true); - expect(firstConnection.metaData!.isClosed, true); - - // has a new connection been created, with a new socket? - OutboundConnection secondConnection = atLookup.connection!; - MockSecureSocket secondSocket = - secondConnection.underlying as MockSecureSocket; - expect(firstConnection.hashCode == secondConnection.hashCode, false); - expect(secondSocket.mockNumber, 2); - expect(secondSocket.destroyed, false); - expect(secondConnection.metaData!.isClosed, false); - expect(secondConnection.isInValid(), false); - }, timeout: Timeout(Duration(minutes: 5))); + String host = 'test.host'; + int port = 64; + + SecureSocket mockSecureSocket = MockSecureSocket(); + MockAtLookupSecureSocketFactory mockAtLookupSecureSocketFactory = + MockAtLookupSecureSocketFactory(); + SecondaryAddressFinder mockSecondaryAddressFinder = + MockSecondaryAddressFinder(); + + AtLookupImpl atLookup = AtLookupImpl('@alice', host, port, + secondaryAddressFinder: mockSecondaryAddressFinder); + SecureSocketConfig secureSocketConfig = SecureSocketConfig(); + + // Setting mock instances + atLookup.atSocketFactory = mockAtLookupSecureSocketFactory; + + // Set mock responses. + when(() => mockAtLookupSecureSocketFactory.createUnderlying( + host, '$port', secureSocketConfig)) + .thenAnswer((_) => Future.value(mockSecureSocket)); + + when(() => mockSecureSocket.setOption(SocketOption.tcpNoDelay, true)) + .thenReturn(true); + + // In the constructor of [BaseConnection] which is super class of [OutboundConnectionImpl] + // socket.setOption is invoked. Therefore, the initialization of outboundConnectionImpl + // should be executed after when(() => mockSecureSocket.setOption). + // Otherwise a null exception arises. + OutboundConnectionImpl outboundConnectionImpl = + OutboundConnectionImpl(mockSecureSocket); + + when(() => + mockAtLookupSecureSocketFactory.outBoundConnectionFactory( + mockSecureSocket)).thenReturn(outboundConnectionImpl); + + when(() => mockAtLookupSecureSocketFactory + .atLookupSocketListenerFactory(outboundConnectionImpl)) + .thenReturn(OutboundMessageListener(outboundConnectionImpl)); + + // Setting connection timeout to 2 seconds. + atLookup.outboundConnectionTimeout = + Duration(seconds: 2).inMilliseconds; + // Create outbound connection. + bool isConnCreated = await atLookup.createOutboundConnection( + host, '$port', secureSocketConfig); + expect(isConnCreated, true); + expect(atLookup.connection?.isInValid(), false); + // Wait for the connection to timeout. + await Future.delayed(Duration(seconds: 2)); + expect(atLookup.connection?.isInValid(), true); + }); + + // test( + // 'test AtLookupImpl closes invalid connections before creating new ones', + // () async { + // AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, + // secondaryAddressFinder: finder); + // expect(atLookup.atSocketFactory.runtimeType.toString(), + // "AtLookupSecureSocketFactory"); + // + // // Override atConnectionFactory with mock in AtLookupImpl + // atLookup.atSocketFactory = mockOutboundConnectionFactory; + // + // when(() => mockOutboundConnection.underlying) + // .thenAnswer((_) => mockSecureSocket); + // + // when(() => mockOutboundConnectionFactory.outBoundConnectionFactory( + // mockSecureSocket)).thenReturn(mockOutboundConnection); + // + // when(() => mockOutboundConnectionFactory.atLookupSocketListenerFactory( + // mockOutboundConnection)).thenAnswer((_) => mockOutboundListener); + // + // AtConnectionMetaData outboundConnectionMetadata = + // OutboundConnectionMetadata(); + // + // when(() => mockOutboundConnection.metaData) + // .thenAnswer((_) => outboundConnectionMetadata); + // when(() => mockOutboundConnection.isInValid()) + // .thenAnswer((_) => (mockSecureSocket as MockSecureSocket).destroyed); + // + // await atLookup.createConnection(); + // + // // let's get a handle to the first socket & connection + // OutboundConnection firstConnection = atLookup.connection!; + // MockSecureSocket firstSocket = + // firstConnection.underlying as MockSecureSocket; + // + // expect(firstSocket.mockNumber, 1); + // expect(firstSocket.destroyed, false); + // expect(firstConnection.metaData!.isClosed, false); + // expect(firstConnection.isInValid(), false); + // + // // Make the connection appear 'idle' + // firstConnection.setIdleTime(1); + // await Future.delayed(Duration(milliseconds: 2)); + // expect(firstConnection.isInValid(), true); + // + // // When we now call AtLookupImpl's createConnection again, it should: + // // - notice that its current connection is 'idle', and close it + // // - create a new connection + // await atLookup.createConnection(); + // + // // has the first connection been closed, and its socket destroyed? + // expect(firstSocket.destroyed, true); + // expect(firstConnection.metaData!.isClosed, true); + // + // // has a new connection been created, with a new socket? + // OutboundConnection secondConnection = atLookup.connection!; + // MockSecureSocket secondSocket = + // secondConnection.underlying as MockSecureSocket; + // expect(firstConnection.hashCode == secondConnection.hashCode, false); + // expect(secondSocket.mockNumber, 2); + // expect(secondSocket.destroyed, false); + // expect(secondConnection.metaData!.isClosed, false); + // expect(secondConnection.isInValid(), false); + // }, timeout: Timeout(Duration(minutes: 5))); test( 'test message listener closes connection' From 4f23462c4522a3acb0f4ea843c5891d4adb1a5c4 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Thu, 14 Nov 2024 03:11:17 +0530 Subject: [PATCH 13/23] fixed failing unit test --- .../at_lookup/lib/src/at_lookup_impl.dart | 8 +- .../test/connection_management_test.dart | 177 ++++++++---------- .../test/at_lookup_test.dart | 11 ++ 3 files changed, 98 insertions(+), 98 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 252fb602..95697c3d 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -648,20 +648,20 @@ class AtLookupImpl implements AtLookUp { String host, String port, SecureSocketConfig secureSocketConfig) async { try { // Create the socket connection using the factory - final connection = await atSocketFactory.createUnderlying( + final socket = await atSocketFactory.createUnderlying( host, port, secureSocketConfig); // Create the outbound connection and listener using the factory's methods final outboundConnection = - atSocketFactory.outBoundConnectionFactory(connection); + atSocketFactory.outBoundConnectionFactory(socket); messageListener = atSocketFactory.atLookupSocketListenerFactory(outboundConnection); // Set the connection type in `_webSocketConnection` or `_connection` - if (connection is WebSocket) { + if (socket is WebSocket) { _webSocketConnection = outboundConnection as OutboundWebSocketConnection; - } else if (connection is SecureSocket) { + } else if (socket is SecureSocket) { _connection = outboundConnection as OutboundConnection; } diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index 35dd5655..df40c854 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; -import 'package:at_utils/at_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; @@ -17,30 +16,37 @@ class MockAtLookupSecureSocketFactory extends Mock implements AtLookupSecureSocketFactory {} void main() { - AtSignLogger.root_level = 'finest'; group('test connection close and socket cleanup', () { late SecondaryAddressFinder finder; late MockAtLookupOutboundConnectionFactory mockOutboundConnectionFactory; - late SecureSocket mockSecureSocket; late OutboundMessageListener mockOutboundListener; - late MockOutboundConnectionImpl mockOutboundConnection; setUp(() { + mockSocketNumber = 1; // Reset counter for each test finder = MockSecondaryAddressFinder(); - mockOutboundListener = MockOutboundMessageListener(); - // Mock outbound connection creation for the first socket - mockOutboundConnection = MockOutboundConnectionImpl(); - mockSecureSocket = createMockAtServerSocket('test.test.test', 12345); when(() => finder.findSecondary(any())) .thenAnswer((_) async => SecondaryAddress('test.test.test', 12345)); mockOutboundConnectionFactory = MockAtLookupOutboundConnectionFactory(); registerFallbackValue(SecureSocketConfig()); - mockSocketNumber = 1; + mockOutboundListener = MockOutboundMessageListener(); + when(() => mockOutboundConnectionFactory.createUnderlying( - 'test.test.test', '12345', any())).thenAnswer((invocation) { - return Future.value(mockSecureSocket); + 'test.test.test', '12345', any())).thenAnswer((_) { + // Each call provides a new instance of MockSecureSocket + SecureSocket newMockSocket = + createMockAtServerSocket('test.test.test', 12345); + OutboundConnectionImpl newOutboundConnectionImpl = + OutboundConnectionImpl(newMockSocket); + + // Update factory to return this new connection and listener + when(() => mockOutboundConnectionFactory.outBoundConnectionFactory( + newMockSocket)).thenReturn(newOutboundConnectionImpl); + + when(() => mockOutboundConnectionFactory.atLookupSocketListenerFactory( + newOutboundConnectionImpl)).thenAnswer((_) => mockOutboundListener); + return Future.value(newMockSocket); }); }); @@ -63,9 +69,9 @@ void main() { int port = 64; SecureSocket mockSecureSocket = MockSecureSocket(); - MockAtLookupSecureSocketFactory mockAtLookupSecureSocketFactory = + MockAtLookupSecureSocketFactory mockAtLookupSecureSocketFactory = MockAtLookupSecureSocketFactory(); - SecondaryAddressFinder mockSecondaryAddressFinder = + SecondaryAddressFinder mockSecondaryAddressFinder = MockSecondaryAddressFinder(); AtLookupImpl atLookup = AtLookupImpl('@alice', host, port, @@ -73,7 +79,7 @@ void main() { SecureSocketConfig secureSocketConfig = SecureSocketConfig(); // Setting mock instances - atLookup.atSocketFactory = mockAtLookupSecureSocketFactory; + atLookup.atSocketFactory = mockAtLookupSecureSocketFactory; // Set mock responses. when(() => mockAtLookupSecureSocketFactory.createUnderlying( @@ -81,7 +87,7 @@ void main() { .thenAnswer((_) => Future.value(mockSecureSocket)); when(() => mockSecureSocket.setOption(SocketOption.tcpNoDelay, true)) - .thenReturn(true); + .thenReturn(true); // In the constructor of [BaseConnection] which is super class of [OutboundConnectionImpl] // socket.setOption is invoked. Therefore, the initialization of outboundConnectionImpl @@ -90,91 +96,74 @@ void main() { OutboundConnectionImpl outboundConnectionImpl = OutboundConnectionImpl(mockSecureSocket); - when(() => - mockAtLookupSecureSocketFactory.outBoundConnectionFactory( - mockSecureSocket)).thenReturn(outboundConnectionImpl); + when(() => mockAtLookupSecureSocketFactory.outBoundConnectionFactory( + mockSecureSocket)).thenReturn(outboundConnectionImpl); when(() => mockAtLookupSecureSocketFactory .atLookupSocketListenerFactory(outboundConnectionImpl)) .thenReturn(OutboundMessageListener(outboundConnectionImpl)); // Setting connection timeout to 2 seconds. - atLookup.outboundConnectionTimeout = - Duration(seconds: 2).inMilliseconds; - // Create outbound connection. - bool isConnCreated = await atLookup.createOutboundConnection( - host, '$port', secureSocketConfig); - expect(isConnCreated, true); - expect(atLookup.connection?.isInValid(), false); - // Wait for the connection to timeout. - await Future.delayed(Duration(seconds: 2)); - expect(atLookup.connection?.isInValid(), true); - }); - - // test( - // 'test AtLookupImpl closes invalid connections before creating new ones', - // () async { - // AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, - // secondaryAddressFinder: finder); - // expect(atLookup.atSocketFactory.runtimeType.toString(), - // "AtLookupSecureSocketFactory"); - // - // // Override atConnectionFactory with mock in AtLookupImpl - // atLookup.atSocketFactory = mockOutboundConnectionFactory; - // - // when(() => mockOutboundConnection.underlying) - // .thenAnswer((_) => mockSecureSocket); - // - // when(() => mockOutboundConnectionFactory.outBoundConnectionFactory( - // mockSecureSocket)).thenReturn(mockOutboundConnection); - // - // when(() => mockOutboundConnectionFactory.atLookupSocketListenerFactory( - // mockOutboundConnection)).thenAnswer((_) => mockOutboundListener); - // - // AtConnectionMetaData outboundConnectionMetadata = - // OutboundConnectionMetadata(); - // - // when(() => mockOutboundConnection.metaData) - // .thenAnswer((_) => outboundConnectionMetadata); - // when(() => mockOutboundConnection.isInValid()) - // .thenAnswer((_) => (mockSecureSocket as MockSecureSocket).destroyed); - // - // await atLookup.createConnection(); - // - // // let's get a handle to the first socket & connection - // OutboundConnection firstConnection = atLookup.connection!; - // MockSecureSocket firstSocket = - // firstConnection.underlying as MockSecureSocket; - // - // expect(firstSocket.mockNumber, 1); - // expect(firstSocket.destroyed, false); - // expect(firstConnection.metaData!.isClosed, false); - // expect(firstConnection.isInValid(), false); - // - // // Make the connection appear 'idle' - // firstConnection.setIdleTime(1); - // await Future.delayed(Duration(milliseconds: 2)); - // expect(firstConnection.isInValid(), true); - // - // // When we now call AtLookupImpl's createConnection again, it should: - // // - notice that its current connection is 'idle', and close it - // // - create a new connection - // await atLookup.createConnection(); - // - // // has the first connection been closed, and its socket destroyed? - // expect(firstSocket.destroyed, true); - // expect(firstConnection.metaData!.isClosed, true); - // - // // has a new connection been created, with a new socket? - // OutboundConnection secondConnection = atLookup.connection!; - // MockSecureSocket secondSocket = - // secondConnection.underlying as MockSecureSocket; - // expect(firstConnection.hashCode == secondConnection.hashCode, false); - // expect(secondSocket.mockNumber, 2); - // expect(secondSocket.destroyed, false); - // expect(secondConnection.metaData!.isClosed, false); - // expect(secondConnection.isInValid(), false); - // }, timeout: Timeout(Duration(minutes: 5))); + atLookup.outboundConnectionTimeout = Duration(seconds: 2).inMilliseconds; + // Create outbound connection. + bool isConnCreated = await atLookup.createOutboundConnection( + host, '$port', secureSocketConfig); + expect(isConnCreated, true); + expect(atLookup.connection?.isInValid(), false); + // Wait for the connection to timeout. + await Future.delayed(Duration(seconds: 2)); + expect(atLookup.connection?.isInValid(), true); + }); + + test( + 'test AtLookupImpl closes invalid connections before creating new ones', + () async { + AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, + secondaryAddressFinder: finder); + expect(atLookup.atSocketFactory.runtimeType.toString(), + "AtLookupSecureSocketFactory"); + + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockOutboundConnectionFactory; + + await atLookup.createConnection(); + + // let's get a handle to the first socket & connection + OutboundConnection firstConnection = atLookup.connection!; + MockSecureSocket firstSocket = + firstConnection.underlying as MockSecureSocket; + + expect(firstSocket.mockNumber, 1); + expect(firstSocket.destroyed, false); + expect(firstConnection.metaData!.isClosed, false); + expect(firstConnection.isInValid(), false); + + // Make the connection appear 'idle' + firstConnection.setIdleTime(1); + await Future.delayed(Duration(milliseconds: 2)); + expect(firstConnection.isInValid(), true); + + atLookup.atSocketFactory = mockOutboundConnectionFactory; + + // When we now call AtLookupImpl's createConnection again, it should: + // - notice that its current connection is 'idle', and close it + // - create a new connection + await atLookup.createConnection(); + + // has the first connection been closed, and its socket destroyed? + expect(firstSocket.destroyed, true); + expect(firstConnection.metaData!.isClosed, true); + + // has a new connection been created, with a new socket? + OutboundConnection secondConnection = atLookup.connection!; + MockSecureSocket secondSocket = + secondConnection.underlying as MockSecureSocket; + expect(firstConnection.hashCode == secondConnection.hashCode, false); + expect(secondSocket.mockNumber, 2); + expect(secondSocket.destroyed, false); + expect(secondConnection.metaData!.isClosed, false); + expect(secondConnection.isInValid(), false); + }); test( 'test message listener closes connection' diff --git a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart index 27612487..131d67e6 100644 --- a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart +++ b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart @@ -22,6 +22,17 @@ void main() { expect(response, contains('public:signing_publickey$atSign')); }); + test( + 'A test to verify a socket connection by passing useWebSocket to false and do a cram authenticate and scan', + () async { + var atLookup = + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); + var command = 'scan\n'; + var response = await atLookup.executeCommand(command, auth: true); + expect(response, contains('public:signing_publickey$atSign')); + }); + test( 'A test to verify a websocket connection and do a cram authenticate and update', () async { From 95fc90c5353ba676c78e16d89022030a53edccbe Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Thu, 14 Nov 2024 03:24:35 +0530 Subject: [PATCH 14/23] pubspec --- tests/at_onboarding_cli_functional_tests/pubspec.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/at_onboarding_cli_functional_tests/pubspec.yaml b/tests/at_onboarding_cli_functional_tests/pubspec.yaml index ed30a139..f85190eb 100644 --- a/tests/at_onboarding_cli_functional_tests/pubspec.yaml +++ b/tests/at_onboarding_cli_functional_tests/pubspec.yaml @@ -18,6 +18,13 @@ dependency_overrides: path: ../../packages/at_onboarding_cli at_commons: path: ../../packages/at_commons + at_lookup: + path: ../../packages/at_lookup + at_client: + git: + url: https://github.com/atsign-foundation/at_client_sdk + path: packages/at_client + ref: websocket_test dev_dependencies: lints: ^1.0.0 From a6350413676dc1812c2f634d193e89434bdbe0bc Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Thu, 14 Nov 2024 03:36:00 +0530 Subject: [PATCH 15/23] correct atsign and removed commented code --- packages/at_lookup/lib/src/connection/base_connection.dart | 1 - .../at_onboarding_cli_functional_tests/test/at_lookup_test.dart | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/at_lookup/lib/src/connection/base_connection.dart b/packages/at_lookup/lib/src/connection/base_connection.dart index 3263f83d..2e5cf7a5 100644 --- a/packages/at_lookup/lib/src/connection/base_connection.dart +++ b/packages/at_lookup/lib/src/connection/base_connection.dart @@ -8,7 +8,6 @@ import 'package:at_utils/at_logger.dart'; abstract class BaseConnection extends AtConnection { final T _socket; late final AtSignLogger logger; - // late final Socket _socket; StringBuffer? buffer; AtConnectionMetaData? metaData; diff --git a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart index 131d67e6..4991997f 100644 --- a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart +++ b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart @@ -7,7 +7,7 @@ import 'package:at_chops/at_chops.dart'; import 'package:test/test.dart'; void main() { - String atSign = '@bob'; + String atSign = '@bob🛠'; AtChops atChopsKeys = createAtChopsFromDemoKeys(atSign); group('A group of tests to assert on authenticate functionality', () { From 65f6141df3e076d20c2cd3a747d28988217e52a6 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Mon, 18 Nov 2024 22:01:54 +0530 Subject: [PATCH 16/23] removed useWebSocket flag and added unit tests --- .../at_lookup/lib/src/at_lookup_impl.dart | 9 +- packages/at_lookup/test/at_lookup_test.dart | 1049 +++++++++++------ .../at_lookup/test/at_lookup_test_utils.dart | 22 + .../test/at_lookup_test.dart | 10 +- 4 files changed, 725 insertions(+), 365 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 95697c3d..c8c635d5 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -16,7 +16,6 @@ import 'package:crypto/crypto.dart'; import 'package:crypton/crypton.dart'; import 'package:mutex/mutex.dart'; - class AtLookupImpl implements AtLookUp { final logger = AtSignLogger('AtLookup'); @@ -63,12 +62,10 @@ class AtLookupImpl implements AtLookUp { SecondaryAddressFinder? secondaryAddressFinder, SecureSocketConfig? secureSocketConfig, Map? clientConfig, - bool useWebSocket = false}) // Add a flag for WebSocket or SecureSocket + AtLookupOutboundConnectionFactory? atSocketFactory}) { - // Determine the factory type based on the useWebSocket flag - atSocketFactory = useWebSocket - ? AtLookupWebSocketFactory() - : AtLookupSecureSocketFactory() as AtLookupOutboundConnectionFactory; + // Default to secure socket factory + this.atSocketFactory = atSocketFactory ?? AtLookupSecureSocketFactory(); _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; diff --git a/packages/at_lookup/test/at_lookup_test.dart b/packages/at_lookup/test/at_lookup_test.dart index 38015029..24abad81 100644 --- a/packages/at_lookup/test/at_lookup_test.dart +++ b/packages/at_lookup/test/at_lookup_test.dart @@ -18,408 +18,749 @@ class FakeAtSigningInput extends Fake implements AtSigningInput {} void main() { AtSignLogger.root_level = 'finest'; late OutboundConnection mockOutBoundConnection; + late OutboundWebSocketConnection mockOutboundWebsocketConnection; late SecondaryAddressFinder mockSecondaryAddressFinder; late OutboundMessageListener mockOutboundListener; late AtLookupOutboundConnectionFactory mockAtConnectionFactory; late AtChops mockAtChops; late SecureSocket mockSecureSocket; + late WebSocket mockWebSocket; String atServerHost = '127.0.0.1'; int atServerPort = 12345; - setUp(() { - mockOutBoundConnection = MockOutboundConnectionImpl(); - mockSecondaryAddressFinder = MockSecondaryAddressFinder(); - mockOutboundListener = MockOutboundMessageListener(); - mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); - mockAtChops = MockAtChops(); - registerFallbackValue(SecureSocketConfig()); - mockSecureSocket = createMockAtServerSocket(atServerHost, atServerPort); - - when(() => mockSecondaryAddressFinder.findSecondary('@alice')) - .thenAnswer((_) async { - return SecondaryAddress(atServerHost, atServerPort); - }); + group('A group of websocket tests', () { + setUp(() { + mockOutBoundConnection = MockOutboundConnectionImpl(); + mockSecondaryAddressFinder = MockSecondaryAddressFinder(); + mockOutboundListener = MockOutboundMessageListener(); + mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); + mockAtChops = MockAtChops(); + registerFallbackValue(SecureSocketConfig()); + mockSecureSocket = createMockAtServerSocket(atServerHost, atServerPort); + + when(() => mockSecondaryAddressFinder.findSecondary('@alice')) + .thenAnswer((_) async { + return SecondaryAddress(atServerHost, atServerPort); + }); - when(() => mockAtConnectionFactory.createUnderlying( - atServerHost, '12345', any())).thenAnswer((_) { - return Future.value(mockSecureSocket); - }); + when(() => mockAtConnectionFactory.createUnderlying( + atServerHost, '12345', any())).thenAnswer((_) { + return Future.value(mockSecureSocket); + }); - when(() => - mockAtConnectionFactory.outBoundConnectionFactory(mockSecureSocket)) - .thenAnswer((_) => mockOutBoundConnection); + when(() => mockAtConnectionFactory.outBoundConnectionFactory( + mockSecureSocket)).thenAnswer((_) => mockOutBoundConnection); - when(() => mockOutBoundConnection.write(any())) - .thenAnswer((_) => Future.value()); + when(() => mockOutBoundConnection.write(any())) + .thenAnswer((_) => Future.value()); - when(() => mockAtConnectionFactory.atLookupSocketListenerFactory( - mockOutBoundConnection)).thenAnswer((_) => mockOutboundListener); - }); + when(() => mockAtConnectionFactory.atLookupSocketListenerFactory( + mockOutBoundConnection)).thenAnswer((_) => mockOutboundListener); + }); - group('A group of tests to verify atlookup pkam authentication', () { - test('pkam auth without enrollmentId - auth success', () async { - final pkamSignature = - 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; - - AtSigningResult mockSigningResult = AtSigningResult() - ..result = 'mock_signing_result'; - registerFallbackValue(FakeAtSigningInput()); - when(() => mockAtChops.sign(any())).thenAnswer((_) => mockSigningResult); - - when(() => mockAtChops.sign(any())) - .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value('data:success')); - - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - when(() => mockOutBoundConnection.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) - .thenAnswer((invocation) { - mockSecureSocket.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n'); - return Future.value(); + group('A group of tests to verify atlookup pkam authentication', () { + test('pkam auth without enrollmentId - auth success', () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value('data:success')); + + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + when(() => mockOutBoundConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) + .thenAnswer((invocation) { + mockSecureSocket.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl( + '@alice', + atServerHost, + 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + var result = await atLookup.pkamAuthenticate(); + expect(result, true); }); - final atLookup = AtLookupImpl( - '@alice', - atServerHost, - 64, - secondaryAddressFinder: mockSecondaryAddressFinder, - ); - // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - var result = await atLookup.pkamAuthenticate(); - expect(result, true); - }); + test('pkam auth without enrollmentId - auth failed', () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()).thenAnswer((_) => + Future.value('error:AT0401-Exception: pkam authentication failed')); + + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + when(() => mockOutBoundConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) + .thenAnswer((invocation) { + mockSecureSocket.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + expect(() async => await atLookup.pkamAuthenticate(), + throwsA(predicate((e) => e is UnAuthenticatedException))); + }); - test('pkam auth without enrollmentId - auth failed', () async { - final pkamSignature = - 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; - - AtSigningResult mockSigningResult = AtSigningResult() - ..result = 'mock_signing_result'; - registerFallbackValue(FakeAtSigningInput()); - when(() => mockAtChops.sign(any())).thenAnswer((_) => mockSigningResult); - - when(() => mockAtChops.sign(any())) - .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()).thenAnswer((_) => - Future.value('error:AT0401-Exception: pkam authentication failed')); - - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - when(() => mockOutBoundConnection.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) - .thenAnswer((invocation) { - mockSecureSocket.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n'); - return Future.value(); + test('pkam auth with enrollmentId - auth success', () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + final enrollmentIdFromServer = '5a21feb4-dc04-4603-829c-15f523789170'; + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value('data:success')); + + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + when(() => mockOutBoundConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) + .thenAnswer((invocation) { + mockSecureSocket.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + var result = await atLookup.pkamAuthenticate( + enrollmentId: enrollmentIdFromServer); + expect(result, true); }); - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - expect(() async => await atLookup.pkamAuthenticate(), - throwsA(predicate((e) => e is UnAuthenticatedException))); + test('pkam auth with enrollmentId - auth failed', () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + final enrollmentIdFromServer = '5a21feb4-dc04-4603-829c-15f523789170'; + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()).thenAnswer((_) => + Future.value('error:AT0401-Exception: pkam authentication failed')); + + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + when(() => mockOutBoundConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) + .thenAnswer((invocation) { + mockSecureSocket.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + expect( + () async => await atLookup.pkamAuthenticate( + enrollmentId: enrollmentIdFromServer), + throwsA(predicate((e) => + e is UnAuthenticatedException && + e.message.contains('AT0401')))); + }); }); - test('pkam auth with enrollmentId - auth success', () async { - final pkamSignature = - 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; - final enrollmentIdFromServer = '5a21feb4-dc04-4603-829c-15f523789170'; - AtSigningResult mockSigningResult = AtSigningResult() - ..result = 'mock_signing_result'; - registerFallbackValue(FakeAtSigningInput()); - when(() => mockAtChops.sign(any())).thenAnswer((_) => mockSigningResult); - - when(() => mockAtChops.sign(any())) - .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value('data:success')); - - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - when(() => mockOutBoundConnection.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) - .thenAnswer((invocation) { - mockSecureSocket.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n'); - return Future.value(); + group('A group of tests to verify executeCommand method', () { + test('executeCommand - from verb - auth false', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + final fromResponse = + 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(fromResponse)); + var result = await atLookup.executeCommand('from:@alice\n'); + expect(result, fromResponse); + }, timeout: Timeout(Duration(minutes: 5))); + + test('executeCommand -llookup verb - auth true - auth key not set', + () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + final fromResponse = 'data:1234'; + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(fromResponse)); + expect( + () async => await atLookup.executeCommand('llookup:phone@alice\n', + auth: true), + throwsA(predicate((e) => e is UnAuthenticatedException))); }); - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - var result = - await atLookup.pkamAuthenticate(enrollmentId: enrollmentIdFromServer); - expect(result, true); - }); + test('executeCommand -llookup verb - auth true - at_chops set', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + final llookupCommand = 'llookup:phone@alice\n'; + final llookupResponse = 'data:1234'; + when(() => mockOutBoundConnection.write(llookupCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(llookupCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(llookupResponse)); + var result = await atLookup.executeCommand(llookupCommand); + expect(result, llookupResponse); + }); - test('pkam auth with enrollmentId - auth failed', () async { - final pkamSignature = - 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; - final enrollmentIdFromServer = '5a21feb4-dc04-4603-829c-15f523789170'; - AtSigningResult mockSigningResult = AtSigningResult() - ..result = 'mock_signing_result'; - registerFallbackValue(FakeAtSigningInput()); - when(() => mockAtChops.sign(any())).thenAnswer((_) => mockSigningResult); - - when(() => mockAtChops.sign(any())) - .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()).thenAnswer((_) => - Future.value('error:AT0401-Exception: pkam authentication failed')); - - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - when(() => mockOutBoundConnection.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) - .thenAnswer((invocation) { - mockSecureSocket.write( - 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n'); - return Future.value(); + test('executeCommand - test non json error handling', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + final llookupCommand = 'llookup:phone@alice\n'; + final llookupResponse = 'error:AT0015-Exception: fubar'; + when(() => mockOutBoundConnection.write(llookupCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(llookupCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(llookupResponse)); + await expectLater( + atLookup.executeCommand(llookupCommand), + throwsA(predicate((e) => + e is AtLookUpException && + e.errorMessage == 'Exception: fubar'))); }); - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - expect( - () async => await atLookup.pkamAuthenticate( - enrollmentId: enrollmentIdFromServer), - throwsA(predicate((e) => - e is UnAuthenticatedException && e.message.contains('AT0401')))); + test('executeCommand - test json error handling', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + final llookupCommand = 'llookup:phone@alice\n'; + final llookupResponse = + 'error:{"errorCode":"AT0015","errorDescription":"Exception: fubar"}'; + when(() => mockOutBoundConnection.write(llookupCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(llookupCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(llookupResponse)); + await expectLater( + atLookup.executeCommand(llookupCommand), + throwsA(predicate((e) => + e is AtLookUpException && + e.errorMessage == 'Exception: fubar'))); + }); }); - }); - group('A group of tests to verify executeCommand method', () { - test('executeCommand - from verb - auth false', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - final fromResponse = - 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(fromResponse)); - var result = await atLookup.executeCommand('from:@alice\n'); - expect(result, fromResponse); - }, timeout: Timeout(Duration(minutes: 5))); - - test('executeCommand -llookup verb - auth true - auth key not set', - () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - final fromResponse = 'data:1234'; - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(fromResponse)); - expect( - () async => await atLookup.executeCommand('llookup:phone@alice\n', - auth: true), - throwsA(predicate((e) => e is UnAuthenticatedException))); - }); + group('Validate executeVerb() behaviour', () { + test('validate EnrollVerbHandler behaviour - request', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + String appName = 'unit_test_1'; + String deviceName = 'test_device'; + String otp = 'ABCDEF'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.request + ..appName = appName + ..deviceName = deviceName + ..otp = otp; + String enrollCommand = + 'enroll:request:{"appName":"$appName","deviceName":"$deviceName","otp":"$otp"}\n'; + final enrollResponse = + 'data:{"enrollmentId":"1234567890","status":"pending"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = false; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + var result = await atLookup.executeVerb(enrollVerbBuilder); + expect(result, enrollResponse); + }); - test('executeCommand -llookup verb - auth true - at_chops set', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - final llookupCommand = 'llookup:phone@alice\n'; - final llookupResponse = 'data:1234'; - when(() => mockOutBoundConnection.write(llookupCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(llookupCommand); - return Future.value(); + test('validate behaviour with EnrollVerbHandler - approve', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atChops = mockAtChops; + atLookup.atSocketFactory = mockAtConnectionFactory; + String appName = 'unit_test_2'; + String deviceName = 'test_device'; + String enrollmentId = '1357913579'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.approve + ..enrollmentId = '1357913579' + ..appName = appName + ..deviceName = deviceName; + String enrollCommand = + 'enroll:approve:{"enrollmentId":"$enrollmentId","appName":"$appName","deviceName":"$deviceName"}\n'; + final enrollResponse = + 'data:{"enrollmentId":"1357913579","status":"approved"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = true; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(llookupResponse)); - var result = await atLookup.executeCommand(llookupCommand); - expect(result, llookupResponse); - }); - test('executeCommand - test non json error handling', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - final llookupCommand = 'llookup:phone@alice\n'; - final llookupResponse = 'error:AT0015-Exception: fubar'; - when(() => mockOutBoundConnection.write(llookupCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(llookupCommand); - return Future.value(); + test('validate behaviour with EnrollVerbHandler - revoke', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atChops = mockAtChops; + atLookup.atSocketFactory = mockAtConnectionFactory; + String enrollmentId = '89213647826348'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.revoke + ..enrollmentId = enrollmentId; + String enrollCommand = + 'enroll:revoke:{"enrollmentId":"$enrollmentId"}\n'; + String enrollResponse = + 'data:{"enrollmentId":"$enrollmentId","status":"revoked"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = true; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(llookupResponse)); - await expectLater( - atLookup.executeCommand(llookupCommand), - throwsA(predicate((e) => - e is AtLookUpException && e.errorMessage == 'Exception: fubar'))); - }); - test('executeCommand - test json error handling', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - final llookupCommand = 'llookup:phone@alice\n'; - final llookupResponse = - 'error:{"errorCode":"AT0015","errorDescription":"Exception: fubar"}'; - when(() => mockOutBoundConnection.write(llookupCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(llookupCommand); - return Future.value(); + test('validate behaviour with EnrollVerbHandler - deny', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + String enrollmentId = '5754765754'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.deny + ..enrollmentId = enrollmentId; + String enrollCommand = 'enroll:deny:{"enrollmentId":"$enrollmentId"}\n'; + String enrollResponse = + 'data:{"enrollmentId":"$enrollmentId","status":"denied"}'; + + when(() => mockOutBoundConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockSecureSocket.write(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = true; + when(() => mockOutBoundConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(llookupResponse)); - await expectLater( - atLookup.executeCommand(llookupCommand), - throwsA(predicate((e) => - e is AtLookUpException && e.errorMessage == 'Exception: fubar'))); }); }); - group('Validate executeVerb() behaviour', () { - test('validate EnrollVerbHandler behaviour - request', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - String appName = 'unit_test_1'; - String deviceName = 'test_device'; - String otp = 'ABCDEF'; - - EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() - ..operation = EnrollOperationEnum.request - ..appName = appName - ..deviceName = deviceName - ..otp = otp; - String enrollCommand = - 'enroll:request:{"appName":"$appName","deviceName":"$deviceName","otp":"$otp"}\n'; - final enrollResponse = - 'data:{"enrollmentId":"1234567890","status":"pending"}'; - - when(() => mockOutBoundConnection.write(enrollCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(enrollCommand); - return Future.value(); + group('A group of web socket tests', () { + setUp(() { + mockOutboundWebsocketConnection = MockOutboundWebsocketConnectionImpl(); + mockSecondaryAddressFinder = MockSecondaryAddressFinder(); + mockOutboundListener = MockOutboundMessageListener(); + mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); + mockAtChops = MockAtChops(); + registerFallbackValue(SecureSocketConfig()); + mockWebSocket = createMockWebSocket(atServerHost, atServerPort); + + when(() => mockSecondaryAddressFinder.findSecondary('@alice')) + .thenAnswer((_) async { + return SecondaryAddress(atServerHost, atServerPort); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() - ..isAuthenticated = false; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - var result = await atLookup.executeVerb(enrollVerbBuilder); - expect(result, enrollResponse); + + when(() => mockAtConnectionFactory.createUnderlying( + atServerHost, '12345', any())).thenAnswer((_) { + return Future.value(mockWebSocket); + }); + + when(() => + mockAtConnectionFactory.outBoundConnectionFactory(mockWebSocket)) + .thenAnswer((_) => mockOutboundWebsocketConnection); + + when(() => mockOutboundWebsocketConnection.write(any())) + .thenAnswer((_) => Future.value()); + + when(() => mockAtConnectionFactory + .atLookupSocketListenerFactory(mockOutboundWebsocketConnection)) + .thenAnswer((_) => mockOutboundListener); }); - test('validate behaviour with EnrollVerbHandler - approve', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atChops = mockAtChops; - atLookup.atSocketFactory = mockAtConnectionFactory; - String appName = 'unit_test_2'; - String deviceName = 'test_device'; - String enrollmentId = '1357913579'; - - EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() - ..operation = EnrollOperationEnum.approve - ..enrollmentId = '1357913579' - ..appName = appName - ..deviceName = deviceName; - String enrollCommand = - 'enroll:approve:{"enrollmentId":"$enrollmentId","appName":"$appName","deviceName":"$deviceName"}\n'; - final enrollResponse = - 'data:{"enrollmentId":"1357913579","status":"approved"}'; - - when(() => mockOutBoundConnection.write(enrollCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(enrollCommand); - return Future.value(); + group('A group of tests to verify atlookup pkam authentication', () { + test('pkam auth using websocket without enrollmentId - auth success', + () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value('data:success')); + + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + when(() => mockOutboundWebsocketConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) + .thenAnswer((invocation) { + mockWebSocket.add( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + var result = await atLookup.pkamAuthenticate(); + expect(result, true); + }, timeout: Timeout(Duration(minutes: 2))); + + test('pkam auth using a websocket without enrollmentId - auth failed', + () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()).thenAnswer((_) => + Future.value('error:AT0401-Exception: pkam authentication failed')); + + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + when(() => mockOutboundWebsocketConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) + .thenAnswer((invocation) { + mockWebSocket.add( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + // Override atConnectionFactory with mock in AtLookupImpl + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + expect(() async => await atLookup.pkamAuthenticate(), + throwsA(predicate((e) => e is UnAuthenticatedException))); + }); + + test('pkam auth using a websocket with enrollmentId - auth success', + () async { + final pkamSignature = + 'MbNbIwCSxsHxm4CHyakSE2yLqjjtnmzpSLPcGG7h+4M/GQAiJkklQfd/x9z58CSJfuSW8baIms26SrnmuYePZURfp5oCqtwRpvt+l07Gnz8aYpXH0k5qBkSR34SBk4nb+hdAjsXXgfWWC56gROPMwpOEbuDS6esU7oku+a7Rdr10xrFlk1Tf2eRwPOMWyuKwOvLwSgyq/INAFRYav5RmLFiecQhPME6ssc1jW92wztylKBtuZT4rk8787b6Z9StxT4dPZzWjfV1+oYDLaqu2PcQS2ZthH+Wj8NgoogDxSP+R7BE1FOVJKnavpuQWeOqNWeUbKkSVP0B0DN6WopAdsg=='; + final enrollmentIdFromServer = '5a21feb4-dc04-4603-829c-15f523789170'; + AtSigningResult mockSigningResult = AtSigningResult() + ..result = 'mock_signing_result'; + registerFallbackValue(FakeAtSigningInput()); + when(() => mockAtChops.sign(any())) + .thenAnswer((_) => mockSigningResult); + + when(() => mockAtChops.sign(any())) + .thenReturn(AtSigningResult()..result = pkamSignature); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value('data:success')); + + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + when(() => mockOutboundWebsocketConnection.write( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) + .thenAnswer((invocation) { + mockWebSocket.add( + 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n'); + return Future.value(); + }); + + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + var result = await atLookup.pkamAuthenticate( + enrollmentId: enrollmentIdFromServer); + expect(result, true); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() - ..isAuthenticated = true; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); - test('validate behaviour with EnrollVerbHandler - revoke', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atChops = mockAtChops; - atLookup.atSocketFactory = mockAtConnectionFactory; - String enrollmentId = '89213647826348'; - - EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() - ..operation = EnrollOperationEnum.revoke - ..enrollmentId = enrollmentId; - String enrollCommand = 'enroll:revoke:{"enrollmentId":"$enrollmentId"}\n'; - String enrollResponse = - 'data:{"enrollmentId":"$enrollmentId","status":"revoked"}'; - - when(() => mockOutBoundConnection.write(enrollCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(enrollCommand); - return Future.value(); + group('A group of tests to verify executeCommand method', () { + test('executeCommand using websocket- from verb - auth false', () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + atLookup.atSocketFactory = mockAtConnectionFactory; + final fromResponse = + 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(fromResponse)); + var result = await atLookup.executeCommand('from:@alice\n'); + expect(result, fromResponse); + }, timeout: Timeout(Duration(minutes: 5))); + + test( + 'executeCommand using websocket-llookup verb - auth true - auth key not set', + () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + final fromResponse = 'data:1234'; + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(fromResponse)); + expect( + () async => await atLookup.executeCommand('llookup:phone@alice\n', + auth: true), + throwsA(predicate((e) => e is UnAuthenticatedException))); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() - ..isAuthenticated = true; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); - test('validate behaviour with EnrollVerbHandler - deny', () async { - final atLookup = AtLookupImpl('@alice', atServerHost, 64, - secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; - atLookup.atChops = mockAtChops; - String enrollmentId = '5754765754'; - - EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() - ..operation = EnrollOperationEnum.deny - ..enrollmentId = enrollmentId; - String enrollCommand = 'enroll:deny:{"enrollmentId":"$enrollmentId"}\n'; - String enrollResponse = - 'data:{"enrollmentId":"$enrollmentId","status":"denied"}'; - - when(() => mockOutBoundConnection.write(enrollCommand)) - .thenAnswer((invocation) { - mockSecureSocket.write(enrollCommand); - return Future.value(); + group('Validate executeVerb() behaviour', () { + test( + 'validate EnrollVerbHandler behaviour using a websocket connection- request', + () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + atLookup.atSocketFactory = mockAtConnectionFactory; + String appName = 'unit_test_1'; + String deviceName = 'test_device'; + String otp = 'ABCDEF'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.request + ..appName = appName + ..deviceName = deviceName + ..otp = otp; + String enrollCommand = + 'enroll:request:{"appName":"$appName","deviceName":"$deviceName","otp":"$otp"}\n'; + final enrollResponse = + 'data:{"enrollmentId":"1234567890","status":"pending"}'; + + when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockWebSocket.add(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = false; + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + var result = await atLookup.executeVerb(enrollVerbBuilder); + expect(result, enrollResponse); + }); + + test( + 'validate behaviour with EnrollVerbHandler using a websocket connection- approve', + () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + atLookup.atChops = mockAtChops; + atLookup.atSocketFactory = mockAtConnectionFactory; + String appName = 'unit_test_2'; + String deviceName = 'test_device'; + String enrollmentId = '1357913579'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.approve + ..enrollmentId = '1357913579' + ..appName = appName + ..deviceName = deviceName; + String enrollCommand = + 'enroll:approve:{"enrollmentId":"$enrollmentId","appName":"$appName","deviceName":"$deviceName"}\n'; + final enrollResponse = + 'data:{"enrollmentId":"1357913579","status":"approved"}'; + + when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockWebSocket.add(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = true; + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); + }); + + test( + 'validate behaviour with EnrollVerbHandler using a websocket connection - revoke', + () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + atLookup.atChops = mockAtChops; + atLookup.atSocketFactory = mockAtConnectionFactory; + String enrollmentId = '89213647826348'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.revoke + ..enrollmentId = enrollmentId; + String enrollCommand = + 'enroll:revoke:{"enrollmentId":"$enrollmentId"}\n'; + String enrollResponse = + 'data:{"enrollmentId":"$enrollmentId","status":"revoked"}'; + + when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockWebSocket.add(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = true; + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); + }); + + test( + 'validate behaviour with EnrollVerbHandler using a websocket connection - deny', + () async { + final atLookup = AtLookupImpl('@alice', atServerHost, 64, + secondaryAddressFinder: mockSecondaryAddressFinder, + atSocketFactory: AtLookupWebSocketFactory()); + atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atChops = mockAtChops; + String enrollmentId = '5754765754'; + + EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() + ..operation = EnrollOperationEnum.deny + ..enrollmentId = enrollmentId; + String enrollCommand = 'enroll:deny:{"enrollmentId":"$enrollmentId"}\n'; + String enrollResponse = + 'data:{"enrollmentId":"$enrollmentId","status":"denied"}'; + + when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + .thenAnswer((invocation) { + mockWebSocket.add(enrollCommand); + return Future.value(); + }); + when(() => mockOutboundListener.read()) + .thenAnswer((_) => Future.value(enrollResponse)); + AtConnectionMetaData? atConnectionMetaData = + OutboundConnectionMetadata()..isAuthenticated = true; + when(() => mockOutboundWebsocketConnection.getMetaData()) + .thenReturn(atConnectionMetaData); + when(() => mockOutboundWebsocketConnection.isInValid()) + .thenReturn(false); + + expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); - when(() => mockOutboundListener.read()) - .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = OutboundConnectionMetadata() - ..isAuthenticated = true; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - - expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); }); } diff --git a/packages/at_lookup/test/at_lookup_test_utils.dart b/packages/at_lookup/test/at_lookup_test_utils.dart index 3ed78fcd..c9a4b648 100644 --- a/packages/at_lookup/test/at_lookup_test_utils.dart +++ b/packages/at_lookup/test/at_lookup_test_utils.dart @@ -23,6 +23,11 @@ class MockSecureSocket extends Mock implements SecureSocket { int mockNumber = mockSocketNumber++; } +class MockWebSocket extends Mock implements WebSocket { + bool destroyed = false; + int mockNumber = mockSocketNumber++; +} + class MockOutboundMessageListener extends Mock implements OutboundMessageListener {} @@ -31,6 +36,9 @@ class MockAtChops extends Mock implements AtChopsImpl {} class MockOutboundConnectionImpl extends Mock implements OutboundConnectionImpl {} +class MockOutboundWebsocketConnectionImpl extends Mock + implements OutboundWebsocketConnectionImpl {} + SecureSocket createMockAtServerSocket(String address, int port) { SecureSocket mss = MockSecureSocket(); when(() => mss.destroy()).thenAnswer((invocation) { @@ -44,3 +52,17 @@ SecureSocket createMockAtServerSocket(String address, int port) { onDone: any(named: "onDone"))).thenReturn(MockStreamSubscription()); return mss; } + +WebSocket createMockWebSocket(String address, int port) { + var mockWebSocket = MockWebSocket(); + when(() => mockWebSocket.close(any(), any())).thenAnswer((_) async { + (mockWebSocket).destroyed = true; + }); + when(() => mockWebSocket.add(any())).thenReturn(null); + when(() => mockWebSocket.listen(any(), + onError: any(named: "onError"), + onDone: any(named: "onDone"), + cancelOnError: any(named: "cancelOnError"))) + .thenReturn(MockStreamSubscription()); + return mockWebSocket; +} diff --git a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart index 4991997f..26ff2266 100644 --- a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart +++ b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart @@ -15,7 +15,7 @@ void main() { 'A test to verify a websocket connection and do a cram authenticate and scan', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -26,7 +26,7 @@ void main() { 'A test to verify a socket connection by passing useWebSocket to false and do a cram authenticate and scan', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -37,7 +37,7 @@ void main() { 'A test to verify a websocket connection and do a cram authenticate and update', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); // update public and private keys manually var command = @@ -56,7 +56,7 @@ void main() { 'A test to verify a websocket connection and do a pkam authenticate and executeCommand', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); atLookup.atChops = atChopsKeys; await atLookup.pkamAuthenticate(); var command = 'update:public:username$atSign bob123\n'; @@ -69,7 +69,7 @@ void main() { 'A test to verify a websocket connection and do a pkam authenticate and execute verb', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, useWebSocket: true); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); atLookup.atChops = atChopsKeys; await atLookup.pkamAuthenticate(); var atKey = 'key1'; From 61e2913f607e6089e925b9434114dc64f012121e Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Tue, 19 Nov 2024 04:16:45 +0530 Subject: [PATCH 17/23] renamed variables and minor changes --- .../at_lookup/lib/src/at_lookup_impl.dart | 64 +++++++------------ .../connection/outbound_message_listener.dart | 23 ++----- packages/at_lookup/test/at_lookup_test.dart | 58 ++++++++--------- .../test/connection_management_test.dart | 10 +-- .../test/at_lookup_test.dart | 10 +-- 5 files changed, 68 insertions(+), 97 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index c8c635d5..90572c62 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -22,15 +22,15 @@ class AtLookupImpl implements AtLookUp { /// Listener for reading verb responses from the remote server late OutboundMessageListener messageListener; - OutboundConnection? _connection; + OutboundConnection? _socketConnection; - OutboundConnection? get connection => _connection; + OutboundConnection? get connection => _socketConnection; OutboundWebSocketConnection? _webSocketConnection; OutboundWebSocketConnection? get webSocketConnection => _webSocketConnection; - late AtLookupOutboundConnectionFactory atSocketFactory; + late AtLookupOutboundConnectionFactory atOutboundConnectionFactory; @override late SecondaryAddressFinder secondaryAddressFinder; @@ -62,10 +62,10 @@ class AtLookupImpl implements AtLookUp { SecondaryAddressFinder? secondaryAddressFinder, SecureSocketConfig? secureSocketConfig, Map? clientConfig, - AtLookupOutboundConnectionFactory? atSocketFactory}) + AtLookupOutboundConnectionFactory? atOutboundConnectionFactory}) { // Default to secure socket factory - this.atSocketFactory = atSocketFactory ?? AtLookupSecureSocketFactory(); + this.atOutboundConnectionFactory = atOutboundConnectionFactory ?? AtLookupSecureSocketFactory(); _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; @@ -237,10 +237,10 @@ class AtLookupImpl implements AtLookUp { Future createConnection() async { if (!isConnectionAvailable()) { - if (_connection != null) { + if (connection != null) { // Clean up the connection before creating a new one logger.finer('Closing old connection'); - await _connection!.close(); + await connection!.close(); } logger.info('Creating new connection'); @@ -435,7 +435,7 @@ class AtLookupImpl implements AtLookUp { await createConnection(); try { await _pkamAuthenticationMutex.acquire(); - if (!_connection!.getMetaData()!.isAuthenticated) { + if (!connection!.getMetaData()!.isAuthenticated) { await _sendCommand((FromVerbBuilder() ..atSign = _currentAtSign ..clientConfig = _clientConfig) @@ -457,13 +457,13 @@ class AtLookupImpl implements AtLookUp { var pkamResponse = await messageListener.read(); if (pkamResponse == 'data:success') { logger.info('auth success'); - _connection!.getMetaData()!.isAuthenticated = true; + connection!.getMetaData()!.isAuthenticated = true; } else { throw UnAuthenticatedException( 'Failed connecting to $_currentAtSign. $pkamResponse'); } } - return _connection!.getMetaData()!.isAuthenticated; + return connection!.getMetaData()!.isAuthenticated; } finally { _pkamAuthenticationMutex.release(); } @@ -645,21 +645,21 @@ class AtLookupImpl implements AtLookUp { String host, String port, SecureSocketConfig secureSocketConfig) async { try { // Create the socket connection using the factory - final socket = await atSocketFactory.createUnderlying( + final underlying = await atOutboundConnectionFactory.createUnderlying( host, port, secureSocketConfig); // Create the outbound connection and listener using the factory's methods final outboundConnection = - atSocketFactory.outBoundConnectionFactory(socket); + atOutboundConnectionFactory.outBoundConnectionFactory(underlying); messageListener = - atSocketFactory.atLookupSocketListenerFactory(outboundConnection); + atOutboundConnectionFactory.atLookupSocketListenerFactory(outboundConnection); // Set the connection type in `_webSocketConnection` or `_connection` - if (socket is WebSocket) { + if (underlying is WebSocket) { _webSocketConnection = outboundConnection as OutboundWebSocketConnection; - } else if (socket is SecureSocket) { - _connection = outboundConnection as OutboundConnection; + } else if (underlying is SecureSocket) { + _socketConnection = outboundConnection as OutboundConnection; } // Set idle time if applicable @@ -674,42 +674,24 @@ class AtLookupImpl implements AtLookUp { } /// Helper method to get the current active connection (either WebSocket or regular). - AtConnection? _getCurrentConnection() { - return _webSocketConnection ?? _connection; - } + AtConnection? _getCurrentConnection() => + _webSocketConnection ?? _socketConnection; + /// Checks if the current connection is available and valid. bool isConnectionAvailable() { - // Get the current active connection (WebSocket or regular). final connection = _getCurrentConnection(); - - // Check if the connection is not null and valid. return connection != null && !connection.isInValid(); } - bool isInValid() { - // Get the current active connection (WebSocket or regular). - final connection = _getCurrentConnection(); - - // If no connection is available, consider it invalid. - if (connection == null) { - return true; - } - - // Check if the connection is invalid based on its state. - return connection.isInValid(); - } + /// Checks if the current connection is invalid. + bool isInValid() => _getCurrentConnection()?.isInValid() ?? true; @override Future close() async { - // Get the current active connection (WebSocket or regular). - final connection = _getCurrentConnection(); - - // If there's an active connection, close it. - if (connection != null) { - await connection.close(); - } + await _getCurrentConnection()?.close(); } + Future _sendCommand(String command) async { await createConnection(); logger.finer('SENDING: $command'); diff --git a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart b/packages/at_lookup/lib/src/connection/outbound_message_listener.dart index 4e7b7e4e..468388f9 100644 --- a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart +++ b/packages/at_lookup/lib/src/connection/outbound_message_listener.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; -import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; @@ -31,22 +30,12 @@ class OutboundMessageListener { logger.finest('Calling socket.listen within runZonedGuarded block'); runZonedGuarded(() { - // Check if the underlying connection is a WebSocket or a raw socket - if (_connection.underlying is WebSocket) { - (_connection.underlying as WebSocket).listen( - (dynamic message) => messageHandler(message), - onDone: onSocketDone, - onError: onSocketError, - ); - } else if (_connection.underlying is Socket) { - (_connection.underlying as Socket).listen( - (data) => messageHandler(data), - onDone: onSocketDone, - onError: onSocketError, - ); - } else { - throw UnsupportedError('Unsupported connection type'); - } + final stream = _connection.underlying as Stream; + stream.listen( + messageHandler, + onDone: onSocketDone, + onError: onSocketError, + ); }, (Object error, StackTrace st) { logger.finer('stack trace $st'); logger.warning( diff --git a/packages/at_lookup/test/at_lookup_test.dart b/packages/at_lookup/test/at_lookup_test.dart index 24abad81..1fbe6747 100644 --- a/packages/at_lookup/test/at_lookup_test.dart +++ b/packages/at_lookup/test/at_lookup_test.dart @@ -94,7 +94,7 @@ void main() { 64, secondaryAddressFinder: mockSecondaryAddressFinder); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate(); expect(result, true); @@ -130,7 +130,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect(() async => await atLookup.pkamAuthenticate(), throwsA(predicate((e) => e is UnAuthenticatedException))); @@ -165,7 +165,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate( enrollmentId: enrollmentIdFromServer); @@ -200,7 +200,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect( () async => await atLookup.pkamAuthenticate( @@ -215,7 +215,7 @@ void main() { test('executeCommand - from verb - auth false', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; final fromResponse = 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; when(() => mockOutboundListener.read()) @@ -240,7 +240,7 @@ void main() { test('executeCommand -llookup verb - auth true - at_chops set', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'data:1234'; @@ -258,7 +258,7 @@ void main() { test('executeCommand - test non json error handling', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'error:AT0015-Exception: fubar'; @@ -279,7 +279,7 @@ void main() { test('executeCommand - test json error handling', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = @@ -303,7 +303,7 @@ void main() { test('validate EnrollVerbHandler behaviour - request', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_1'; String deviceName = 'test_device'; String otp = 'ABCDEF'; @@ -339,7 +339,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); atLookup.atChops = mockAtChops; - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_2'; String deviceName = 'test_device'; String enrollmentId = '1357913579'; @@ -374,7 +374,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); atLookup.atChops = mockAtChops; - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; String enrollmentId = '89213647826348'; EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() @@ -404,7 +404,7 @@ void main() { test('validate behaviour with EnrollVerbHandler - deny', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; String enrollmentId = '5754765754'; @@ -497,9 +497,9 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); + atOutboundConnectionFactory: AtLookupWebSocketFactory()); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate(); expect(result, true); @@ -536,9 +536,9 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); + atOutboundConnectionFactory: AtLookupWebSocketFactory()); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect(() async => await atLookup.pkamAuthenticate(), throwsA(predicate((e) => e is UnAuthenticatedException))); @@ -575,8 +575,8 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); - atLookup.atSocketFactory = mockAtConnectionFactory; + atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate( enrollmentId: enrollmentIdFromServer); @@ -588,8 +588,8 @@ void main() { test('executeCommand using websocket- from verb - auth false', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); - atLookup.atSocketFactory = mockAtConnectionFactory; + atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; final fromResponse = 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; when(() => mockOutboundListener.read()) @@ -603,7 +603,7 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); + atOutboundConnectionFactory: AtLookupWebSocketFactory()); final fromResponse = 'data:1234'; when(() => mockOutboundListener.read()) .thenAnswer((_) => Future.value(fromResponse)); @@ -620,8 +620,8 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); - atLookup.atSocketFactory = mockAtConnectionFactory; + atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_1'; String deviceName = 'test_device'; String otp = 'ABCDEF'; @@ -659,9 +659,9 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); + atOutboundConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = mockAtChops; - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_2'; String deviceName = 'test_device'; String enrollmentId = '1357913579'; @@ -698,9 +698,9 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); + atOutboundConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = mockAtChops; - atLookup.atSocketFactory = mockAtConnectionFactory; + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; String enrollmentId = '89213647826348'; EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() @@ -733,8 +733,8 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atSocketFactory: AtLookupWebSocketFactory()); - atLookup.atSocketFactory = mockAtConnectionFactory; + atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; String enrollmentId = '5754765754'; diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index df40c854..90c3f89a 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -56,7 +56,7 @@ void main() { AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, secondaryAddressFinder: finder); - expect(atLookup.atSocketFactory.runtimeType.toString(), + expect(atLookup.atOutboundConnectionFactory.runtimeType.toString(), "AtLookupSecureSocketFactory"); expect(() async => await atLookup.createConnection(), throwsA(predicate((dynamic e) => e is SecondaryConnectException))); @@ -79,7 +79,7 @@ void main() { SecureSocketConfig secureSocketConfig = SecureSocketConfig(); // Setting mock instances - atLookup.atSocketFactory = mockAtLookupSecureSocketFactory; + atLookup.atOutboundConnectionFactory = mockAtLookupSecureSocketFactory; // Set mock responses. when(() => mockAtLookupSecureSocketFactory.createUnderlying( @@ -120,11 +120,11 @@ void main() { () async { AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, secondaryAddressFinder: finder); - expect(atLookup.atSocketFactory.runtimeType.toString(), + expect(atLookup.atOutboundConnectionFactory.runtimeType.toString(), "AtLookupSecureSocketFactory"); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atSocketFactory = mockOutboundConnectionFactory; + atLookup.atOutboundConnectionFactory = mockOutboundConnectionFactory; await atLookup.createConnection(); @@ -143,7 +143,7 @@ void main() { await Future.delayed(Duration(milliseconds: 2)); expect(firstConnection.isInValid(), true); - atLookup.atSocketFactory = mockOutboundConnectionFactory; + atLookup.atOutboundConnectionFactory = mockOutboundConnectionFactory; // When we now call AtLookupImpl's createConnection again, it should: // - notice that its current connection is 'idle', and close it diff --git a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart index 26ff2266..74b93c4b 100644 --- a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart +++ b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart @@ -15,7 +15,7 @@ void main() { 'A test to verify a websocket connection and do a cram authenticate and scan', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atOutboundConnectionFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -26,7 +26,7 @@ void main() { 'A test to verify a socket connection by passing useWebSocket to false and do a cram authenticate and scan', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atOutboundConnectionFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -37,7 +37,7 @@ void main() { 'A test to verify a websocket connection and do a cram authenticate and update', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atOutboundConnectionFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); // update public and private keys manually var command = @@ -56,7 +56,7 @@ void main() { 'A test to verify a websocket connection and do a pkam authenticate and executeCommand', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atOutboundConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = atChopsKeys; await atLookup.pkamAuthenticate(); var command = 'update:public:username$atSign bob123\n'; @@ -69,7 +69,7 @@ void main() { 'A test to verify a websocket connection and do a pkam authenticate and execute verb', () async { var atLookup = - AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atSocketFactory: AtLookupWebSocketFactory()); + AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, atOutboundConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = atChopsKeys; await atLookup.pkamAuthenticate(); var atKey = 'key1'; From b4eef12947eccc735a64cbce12f1e3215cebd732 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Tue, 19 Nov 2024 21:16:24 +0530 Subject: [PATCH 18/23] addressed review comments --- .../at_lookup/lib/src/at_lookup_impl.dart | 65 +++++++++---------- packages/at_lookup/test/at_lookup_test.dart | 2 +- .../test/connection_management_test.dart | 7 +- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 90572c62..bfa98c1b 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -22,13 +22,19 @@ class AtLookupImpl implements AtLookUp { /// Listener for reading verb responses from the remote server late OutboundMessageListener messageListener; - OutboundConnection? _socketConnection; + AtConnection? + _connection; // Single variable for both socket and WebSocket connections - OutboundConnection? get connection => _socketConnection; + AtConnection? get connection => _connection; - OutboundWebSocketConnection? _webSocketConnection; + /// Setters or methods to initialize the connection + void setSocketConnection(OutboundConnection socketConnection) { + _connection = socketConnection; + } - OutboundWebSocketConnection? get webSocketConnection => _webSocketConnection; + void setWebSocketConnection(OutboundWebSocketConnection webSocketConnection) { + _connection = webSocketConnection; + } late AtLookupOutboundConnectionFactory atOutboundConnectionFactory; @@ -62,10 +68,10 @@ class AtLookupImpl implements AtLookUp { SecondaryAddressFinder? secondaryAddressFinder, SecureSocketConfig? secureSocketConfig, Map? clientConfig, - AtLookupOutboundConnectionFactory? atOutboundConnectionFactory}) - { + AtLookupOutboundConnectionFactory? atOutboundConnectionFactory}) { // Default to secure socket factory - this.atOutboundConnectionFactory = atOutboundConnectionFactory ?? AtLookupSecureSocketFactory(); + this.atOutboundConnectionFactory = + atOutboundConnectionFactory ?? AtLookupSecureSocketFactory(); _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; @@ -472,7 +478,6 @@ class AtLookupImpl implements AtLookUp { @override Future pkamAuthenticate({String? enrollmentId}) async { await createConnection(); - final _connection = _getCurrentConnection(); try { await _pkamAuthenticationMutex.acquire(); if (!_connection!.getMetaData()!.isAuthenticated) { @@ -505,13 +510,13 @@ class AtLookupImpl implements AtLookUp { var pkamResponse = await messageListener.read(); if (pkamResponse == 'data:success') { logger.info('auth success'); - _connection.getMetaData()!.isAuthenticated = true; + _connection!.getMetaData()!.isAuthenticated = true; } else { throw UnAuthenticatedException( 'Failed connecting to $_currentAtSign. $pkamResponse'); } } - return _connection.getMetaData()!.isAuthenticated; + return _connection!.getMetaData()!.isAuthenticated; } finally { _pkamAuthenticationMutex.release(); } @@ -522,7 +527,6 @@ class AtLookupImpl implements AtLookUp { @override Future cramAuthenticate(String secret) async { await createConnection(); - final _connection = _getCurrentConnection(); try { await _cramAuthenticationMutex.acquire(); @@ -553,13 +557,13 @@ class AtLookupImpl implements AtLookUp { if (cramResponse == 'data:success') { logger.info('auth success'); - _connection.getMetaData()!.isAuthenticated = true; + _connection!.getMetaData()!.isAuthenticated = true; } else { throw UnAuthenticatedException('Auth failed'); } } - return _connection.getMetaData()!.isAuthenticated; + return _connection!.getMetaData()!.isAuthenticated; } finally { _cramAuthenticationMutex.release(); } @@ -634,11 +638,8 @@ class AtLookupImpl implements AtLookUp { } bool _isAuthRequired() { - // Get the current active connection (WebSocket or regular). - final connection = _getCurrentConnection(); - - // If there is no connection or the connection is not authenticated, auth is required. - return connection == null || !connection.getMetaData()!.isAuthenticated; + return !isConnectionAvailable() || + !(_connection!.getMetaData()!.isAuthenticated); } Future createOutboundConnection( @@ -651,15 +652,14 @@ class AtLookupImpl implements AtLookUp { // Create the outbound connection and listener using the factory's methods final outboundConnection = atOutboundConnectionFactory.outBoundConnectionFactory(underlying); - messageListener = - atOutboundConnectionFactory.atLookupSocketListenerFactory(outboundConnection); + messageListener = atOutboundConnectionFactory + .atLookupSocketListenerFactory(outboundConnection); // Set the connection type in `_webSocketConnection` or `_connection` if (underlying is WebSocket) { - _webSocketConnection = - outboundConnection as OutboundWebSocketConnection; + _connection = outboundConnection as OutboundWebSocketConnection; } else if (underlying is SecureSocket) { - _socketConnection = outboundConnection as OutboundConnection; + _connection = outboundConnection as OutboundConnection; } // Set idle time if applicable @@ -673,30 +673,23 @@ class AtLookupImpl implements AtLookUp { return true; } - /// Helper method to get the current active connection (either WebSocket or regular). - AtConnection? _getCurrentConnection() => - _webSocketConnection ?? _socketConnection; - - /// Checks if the current connection is available and valid. bool isConnectionAvailable() { - final connection = _getCurrentConnection(); - return connection != null && !connection.isInValid(); + return _connection != null && !_connection!.isInValid(); } - /// Checks if the current connection is invalid. - bool isInValid() => _getCurrentConnection()?.isInValid() ?? true; + bool isInValid() { + return _connection!.isInValid(); + } @override Future close() async { - await _getCurrentConnection()?.close(); + await _connection!.close(); } - Future _sendCommand(String command) async { await createConnection(); logger.finer('SENDING: $command'); - final connection = _getCurrentConnection(); - connection!.write(command); + _connection!.write(command); } @override diff --git a/packages/at_lookup/test/at_lookup_test.dart b/packages/at_lookup/test/at_lookup_test.dart index 1fbe6747..f0189dfd 100644 --- a/packages/at_lookup/test/at_lookup_test.dart +++ b/packages/at_lookup/test/at_lookup_test.dart @@ -30,7 +30,7 @@ void main() { String atServerHost = '127.0.0.1'; int atServerPort = 12345; - group('A group of websocket tests', () { + group('A group of secure socket tests', () { setUp(() { mockOutBoundConnection = MockOutboundConnectionImpl(); mockSecondaryAddressFinder = MockSecondaryAddressFinder(); diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index 90c3f89a..15e96909 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -129,7 +129,9 @@ void main() { await atLookup.createConnection(); // let's get a handle to the first socket & connection - OutboundConnection firstConnection = atLookup.connection!; + // Let's get a handle to the first socket & connection + OutboundConnection firstConnection = + atLookup.connection as OutboundConnection; // Explicit cast MockSecureSocket firstSocket = firstConnection.underlying as MockSecureSocket; @@ -155,7 +157,8 @@ void main() { expect(firstConnection.metaData!.isClosed, true); // has a new connection been created, with a new socket? - OutboundConnection secondConnection = atLookup.connection!; + OutboundConnection secondConnection = + atLookup.connection as OutboundConnection; // Explicit cast MockSecureSocket secondSocket = secondConnection.underlying as MockSecureSocket; expect(firstConnection.hashCode == secondConnection.hashCode, false); From be8058bb066b0f152e4a7f8ababf68ffd50f9776 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Fri, 22 Nov 2024 03:39:34 +0530 Subject: [PATCH 19/23] addressed review commits --- packages/at_lookup/CHANGELOG.md | 2 ++ packages/at_lookup/lib/src/at_lookup_impl.dart | 13 +++++++------ packages/at_lookup/pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/at_lookup/CHANGELOG.md b/packages/at_lookup/CHANGELOG.md index 51f2f642..e6bef4fb 100644 --- a/packages/at_lookup/CHANGELOG.md +++ b/packages/at_lookup/CHANGELOG.md @@ -1,3 +1,5 @@ +## 4.0.0 +- feat: Introduce websocket support in at_lookup_impl ## 3.0.49 - build[deps]: Upgraded the following packages: - at_commons to v5.0.0 diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index bfa98c1b..812051b1 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -243,7 +243,7 @@ class AtLookupImpl implements AtLookUp { Future createConnection() async { if (!isConnectionAvailable()) { - if (connection != null) { + if (_connection != null) { // Clean up the connection before creating a new one logger.finer('Closing old connection'); await connection!.close(); @@ -655,12 +655,13 @@ class AtLookupImpl implements AtLookUp { messageListener = atOutboundConnectionFactory .atLookupSocketListenerFactory(outboundConnection); + _connection = outboundConnection; // Set the connection type in `_webSocketConnection` or `_connection` - if (underlying is WebSocket) { - _connection = outboundConnection as OutboundWebSocketConnection; - } else if (underlying is SecureSocket) { - _connection = outboundConnection as OutboundConnection; - } + // if (underlying is WebSocket) { + // _connection = outboundConnection as OutboundWebSocketConnection; + // } else if (underlying is SecureSocket) { + // _connection = outboundConnection as OutboundConnection; + // } // Set idle time if applicable if (outboundConnectionTimeout != null) { diff --git a/packages/at_lookup/pubspec.yaml b/packages/at_lookup/pubspec.yaml index 2e546ece..24d90be9 100644 --- a/packages/at_lookup/pubspec.yaml +++ b/packages/at_lookup/pubspec.yaml @@ -1,6 +1,6 @@ name: at_lookup description: A Dart library that contains the core commands that can be used with a secondary server (scan, update, lookup, llookup, plookup, etc.) -version: 3.0.49 +version: 4.0.0 repository: https://github.com/atsign-foundation/at_libraries homepage: https://atsign.com documentation: https://docs.atsign.com/ From b32009b5def9e0389dce22912bbabcfb14df9680 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Fri, 22 Nov 2024 18:46:50 +0530 Subject: [PATCH 20/23] removed commented code --- packages/at_lookup/lib/src/at_lookup_impl.dart | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 812051b1..b0ddf1a3 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -246,7 +246,7 @@ class AtLookupImpl implements AtLookUp { if (_connection != null) { // Clean up the connection before creating a new one logger.finer('Closing old connection'); - await connection!.close(); + await _connection!.close(); } logger.info('Creating new connection'); @@ -441,7 +441,7 @@ class AtLookupImpl implements AtLookUp { await createConnection(); try { await _pkamAuthenticationMutex.acquire(); - if (!connection!.getMetaData()!.isAuthenticated) { + if (!_connection!.getMetaData()!.isAuthenticated) { await _sendCommand((FromVerbBuilder() ..atSign = _currentAtSign ..clientConfig = _clientConfig) @@ -463,13 +463,13 @@ class AtLookupImpl implements AtLookUp { var pkamResponse = await messageListener.read(); if (pkamResponse == 'data:success') { logger.info('auth success'); - connection!.getMetaData()!.isAuthenticated = true; + _connection!.getMetaData()!.isAuthenticated = true; } else { throw UnAuthenticatedException( 'Failed connecting to $_currentAtSign. $pkamResponse'); } } - return connection!.getMetaData()!.isAuthenticated; + return _connection!.getMetaData()!.isAuthenticated; } finally { _pkamAuthenticationMutex.release(); } @@ -656,12 +656,6 @@ class AtLookupImpl implements AtLookUp { .atLookupSocketListenerFactory(outboundConnection); _connection = outboundConnection; - // Set the connection type in `_webSocketConnection` or `_connection` - // if (underlying is WebSocket) { - // _connection = outboundConnection as OutboundWebSocketConnection; - // } else if (underlying is SecureSocket) { - // _connection = outboundConnection as OutboundConnection; - // } // Set idle time if applicable if (outboundConnectionTimeout != null) { From 9ea94154630f676f08c422e61f0e2adeba240139 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Mon, 25 Nov 2024 08:22:28 +0530 Subject: [PATCH 21/23] review comments --- packages/at_lookup/lib/at_lookup.dart | 2 +- .../src/connection/at_lookup_outbound_connection_factory.dart | 2 +- packages/at_lookup/test/at_lookup_test_utils.dart | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/at_lookup/lib/at_lookup.dart b/packages/at_lookup/lib/at_lookup.dart index d8d992ba..7b3fef04 100644 --- a/packages/at_lookup/lib/at_lookup.dart +++ b/packages/at_lookup/lib/at_lookup.dart @@ -13,5 +13,5 @@ export 'src/monitor_client.dart'; export 'src/cache/secondary_address_finder.dart'; export 'src/cache/cacheable_secondary_address_finder.dart'; export 'src/util/secure_socket_util.dart'; -export 'src/connection/outbound_websocket_connection_impl.dart'; +// export 'src/connection/outbound_websocket_connection_impl.dart'; export 'src/connection/at_lookup_outbound_connection_factory.dart'; \ No newline at end of file diff --git a/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart index 0448cfb4..911a3882 100644 --- a/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart +++ b/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; - +import 'package:at_lookup/src/connection/outbound_websocket_connection_impl.dart'; import 'outbound_message_listener.dart'; /// This factory is responsible for creating the underlying connection, diff --git a/packages/at_lookup/test/at_lookup_test_utils.dart b/packages/at_lookup/test/at_lookup_test_utils.dart index c9a4b648..8733ee35 100644 --- a/packages/at_lookup/test/at_lookup_test_utils.dart +++ b/packages/at_lookup/test/at_lookup_test_utils.dart @@ -5,6 +5,7 @@ import 'package:at_chops/at_chops.dart'; import 'package:at_lookup/at_lookup.dart'; import 'package:at_lookup/src/connection/outbound_message_listener.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:at_lookup/src/connection/outbound_websocket_connection_impl.dart'; int mockSocketNumber = 1; From 5dc5a47bad04c9d9948e2ffa1d6239e8fbd6a461 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Mon, 25 Nov 2024 09:24:46 +0530 Subject: [PATCH 22/23] removed commented code --- packages/at_lookup/lib/at_lookup.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/at_lookup/lib/at_lookup.dart b/packages/at_lookup/lib/at_lookup.dart index 7b3fef04..5efd1104 100644 --- a/packages/at_lookup/lib/at_lookup.dart +++ b/packages/at_lookup/lib/at_lookup.dart @@ -13,5 +13,4 @@ export 'src/monitor_client.dart'; export 'src/cache/secondary_address_finder.dart'; export 'src/cache/cacheable_secondary_address_finder.dart'; export 'src/util/secure_socket_util.dart'; -// export 'src/connection/outbound_websocket_connection_impl.dart'; export 'src/connection/at_lookup_outbound_connection_factory.dart'; \ No newline at end of file From 8e5aaee46deb43c587c34256c4e84c43dd0cff27 Mon Sep 17 00:00:00 2001 From: purnimavenkatasubbu Date: Sat, 7 Dec 2024 02:59:43 +0530 Subject: [PATCH 23/23] changing heirarchy and removeing outboundconnection --- packages/at_lookup/lib/at_lookup.dart | 5 +- .../at_lookup/lib/src/at_lookup_impl.dart | 59 ++-- .../cacheable_secondary_address_finder.dart | 4 +- .../lib/src/connection/at_connection.dart | 44 ++- .../at_lookup_connection_factory.dart | 79 +++++ ...at_lookup_outbound_connection_factory.dart | 77 ----- ...listener.dart => at_message_listener.dart} | 4 +- ...nection.dart => at_socket_connection.dart} | 36 +-- ...tion.dart => at_websocket_connection.dart} | 26 +- .../src/connection/outbound_connection.dart | 11 - .../connection/outbound_connection_impl.dart | 31 -- .../outbound_websocket_connection.dart | 11 - .../outbound_websocket_connection_impl.dart | 33 -- .../at_lookup/lib/src/monitor_client.dart | 22 +- packages/at_lookup/test/at_lookup_test.dart | 303 +++++++++--------- .../at_lookup/test/at_lookup_test_utils.dart | 17 +- .../test/connection_management_test.dart | 187 +++++------ .../test/outbound_message_listener_test.dart | 199 ++++++------ .../test/secondary_address_cache_test.dart | 11 +- .../test/at_lookup_test.dart | 15 +- 20 files changed, 528 insertions(+), 646 deletions(-) create mode 100644 packages/at_lookup/lib/src/connection/at_lookup_connection_factory.dart delete mode 100644 packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart rename packages/at_lookup/lib/src/connection/{outbound_message_listener.dart => at_message_listener.dart} (98%) rename packages/at_lookup/lib/src/connection/{base_connection.dart => at_socket_connection.dart} (57%) rename packages/at_lookup/lib/src/connection/{base_websocket_connection.dart => at_websocket_connection.dart} (68%) delete mode 100644 packages/at_lookup/lib/src/connection/outbound_connection.dart delete mode 100644 packages/at_lookup/lib/src/connection/outbound_connection_impl.dart delete mode 100644 packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart delete mode 100644 packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart diff --git a/packages/at_lookup/lib/at_lookup.dart b/packages/at_lookup/lib/at_lookup.dart index 5efd1104..47fd67c4 100644 --- a/packages/at_lookup/lib/at_lookup.dart +++ b/packages/at_lookup/lib/at_lookup.dart @@ -5,12 +5,9 @@ library at_lookup; export 'src/at_lookup.dart'; export 'src/at_lookup_impl.dart'; -export 'src/connection/outbound_connection.dart'; -export 'src/connection/outbound_websocket_connection.dart'; -export 'src/connection/outbound_connection_impl.dart'; export 'src/exception/at_lookup_exception.dart'; export 'src/monitor_client.dart'; export 'src/cache/secondary_address_finder.dart'; export 'src/cache/cacheable_secondary_address_finder.dart'; export 'src/util/secure_socket_util.dart'; -export 'src/connection/at_lookup_outbound_connection_factory.dart'; \ No newline at end of file +export 'src/connection/at_lookup_connection_factory.dart'; \ No newline at end of file diff --git a/packages/at_lookup/lib/src/at_lookup_impl.dart b/packages/at_lookup/lib/src/at_lookup_impl.dart index 889c9661..1dad843d 100644 --- a/packages/at_lookup/lib/src/at_lookup_impl.dart +++ b/packages/at_lookup/lib/src/at_lookup_impl.dart @@ -10,7 +10,7 @@ import 'package:at_commons/at_builders.dart'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; import 'package:at_lookup/src/connection/at_connection.dart'; -import 'package:at_lookup/src/connection/outbound_message_listener.dart'; +import 'package:at_lookup/src/connection/at_message_listener.dart'; import 'package:at_utils/at_logger.dart'; import 'package:crypto/crypto.dart'; import 'package:crypton/crypton.dart'; @@ -20,14 +20,13 @@ class AtLookupImpl implements AtLookUp { final logger = AtSignLogger('AtLookup'); /// Listener for reading verb responses from the remote server - late OutboundMessageListener messageListener; + late AtMessageListener messageListener; - AtConnection? - _connection; // Single variable for both socket and WebSocket connections + AtConnection? _connection; // Represents Socket or WebSocket connection AtConnection? get connection => _connection; - late AtLookupOutboundConnectionFactory atOutboundConnectionFactory; + late AtLookupConnectionFactory atConnectionFactory; @override late SecondaryAddressFinder secondaryAddressFinder; @@ -44,7 +43,7 @@ class AtLookupImpl implements AtLookUp { String? cramSecret; // ignore: prefer_typing_uninitialized_variables - var outboundConnectionTimeout; + var atConnectionTimeout; late SecureSocketConfig _secureSocketConfig; @@ -59,10 +58,9 @@ class AtLookupImpl implements AtLookUp { SecondaryAddressFinder? secondaryAddressFinder, SecureSocketConfig? secureSocketConfig, Map? clientConfig, - AtLookupOutboundConnectionFactory? atOutboundConnectionFactory}) { + AtLookupConnectionFactory? atConnectionFactory}) { // Default to secure socket factory - this.atOutboundConnectionFactory = - atOutboundConnectionFactory ?? AtLookupSecureSocketFactory(); + this.atConnectionFactory = atConnectionFactory ?? AtLookupSecureSocketFactory(); _currentAtSign = atSign; _rootDomain = rootDomain; _rootPort = rootPort; @@ -248,8 +246,7 @@ class AtLookupImpl implements AtLookUp { var port = secondaryAddress.port; // 2. Create a connection to the secondary server - await createOutboundConnection( - host, port.toString(), _secureSocketConfig); + await createAtConnection(host, port.toString(), _secureSocketConfig); // 3. Listen to server response messageListener.listen(); @@ -432,7 +429,7 @@ class AtLookupImpl implements AtLookUp { await createConnection(); try { await _pkamAuthenticationMutex.acquire(); - if (!_connection!.getMetaData()!.isAuthenticated) { + if (!_connection!.metaData.isAuthenticated) { await _sendCommand((FromVerbBuilder() ..atSign = _currentAtSign ..clientConfig = _clientConfig) @@ -454,13 +451,13 @@ class AtLookupImpl implements AtLookUp { var pkamResponse = await messageListener.read(); if (pkamResponse == 'data:success') { logger.info('auth success'); - _connection!.getMetaData()!.isAuthenticated = true; + _connection!.metaData.isAuthenticated = true; } else { throw UnAuthenticatedException( 'Failed connecting to $_currentAtSign. $pkamResponse'); } } - return _connection!.getMetaData()!.isAuthenticated; + return _connection!.metaData.isAuthenticated; } finally { _pkamAuthenticationMutex.release(); } @@ -471,7 +468,7 @@ class AtLookupImpl implements AtLookUp { await createConnection(); try { await _pkamAuthenticationMutex.acquire(); - if (!_connection!.getMetaData()!.isAuthenticated) { + if (!_connection!.metaData.isAuthenticated) { await _sendCommand((FromVerbBuilder() ..atSign = _currentAtSign ..clientConfig = _clientConfig) @@ -501,13 +498,13 @@ class AtLookupImpl implements AtLookUp { var pkamResponse = await messageListener.read(); if (pkamResponse == 'data:success') { logger.info('auth success'); - _connection!.getMetaData()!.isAuthenticated = true; + _connection!.metaData.isAuthenticated = true; } else { throw UnAuthenticatedException( 'Failed connecting to $_currentAtSign. $pkamResponse'); } } - return _connection!.getMetaData()!.isAuthenticated; + return _connection!.metaData.isAuthenticated; } finally { _pkamAuthenticationMutex.release(); } @@ -521,7 +518,7 @@ class AtLookupImpl implements AtLookUp { try { await _cramAuthenticationMutex.acquire(); - if (!_connection!.getMetaData()!.isAuthenticated) { + if (!_connection!.metaData.isAuthenticated) { // Use the connection and message listener dynamically await _sendCommand((FromVerbBuilder() ..atSign = _currentAtSign @@ -548,13 +545,13 @@ class AtLookupImpl implements AtLookUp { if (cramResponse == 'data:success') { logger.info('auth success'); - _connection!.getMetaData()!.isAuthenticated = true; + _connection!.metaData.isAuthenticated = true; } else { throw UnAuthenticatedException('Auth failed'); } } - return _connection!.getMetaData()!.isAuthenticated; + return _connection!.metaData.isAuthenticated; } finally { _cramAuthenticationMutex.release(); } @@ -629,28 +626,26 @@ class AtLookupImpl implements AtLookUp { } bool _isAuthRequired() { - return !isConnectionAvailable() || - !(_connection!.getMetaData()!.isAuthenticated); + return !isConnectionAvailable() || !(_connection!.metaData.isAuthenticated); } - Future createOutboundConnection( + Future createAtConnection( String host, String port, SecureSocketConfig secureSocketConfig) async { try { // Create the socket connection using the factory - final underlying = await atOutboundConnectionFactory.createUnderlying( + final underlying = await atConnectionFactory.createUnderlying( host, port, secureSocketConfig); - // Create the outbound connection and listener using the factory's methods - final outboundConnection = - atOutboundConnectionFactory.createConnection(underlying); - messageListener = - atOutboundConnectionFactory.createListener(outboundConnection); + // Create at connection and listener using the factory's methods + AtConnection atConnection = + atConnectionFactory.createConnection(underlying); + messageListener = atConnectionFactory.createListener(atConnection); - _connection = outboundConnection; + _connection = atConnection; // Set idle time if applicable - if (outboundConnectionTimeout != null) { - outboundConnection.setIdleTime(outboundConnectionTimeout); + if (atConnectionTimeout != null) { + atConnection.setIdleTime(atConnectionTimeout); } } on SocketException { throw SecondaryConnectException( diff --git a/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart b/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart index 7bc97ca9..04730ace 100644 --- a/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart +++ b/packages/at_lookup/lib/src/cache/cacheable_secondary_address_finder.dart @@ -128,10 +128,10 @@ class SecondaryAddressCacheEntry { class SecondaryUrlFinder { final String _rootDomain; final int _rootPort; - late final AtLookupOutboundConnectionFactory _socketFactory; + late final AtLookupConnectionFactory _socketFactory; SecondaryUrlFinder(this._rootDomain, this._rootPort, - {AtLookupOutboundConnectionFactory? socketFactory}) { + {AtLookupConnectionFactory? socketFactory}) { _socketFactory = socketFactory ?? AtLookupSecureSocketFactory(); } diff --git a/packages/at_lookup/lib/src/connection/at_connection.dart b/packages/at_lookup/lib/src/connection/at_connection.dart index ab888d00..55de3897 100644 --- a/packages/at_lookup/lib/src/connection/at_connection.dart +++ b/packages/at_lookup/lib/src/connection/at_connection.dart @@ -4,22 +4,52 @@ abstract class AtConnection { /// The underlying connection T get underlying; - /// Write a data to the underlying socket of the connection + /// Metadata for the connection + final AtConnectionMetaData metaData = AtConnectionMetaData(); + + /// The idle timeout in milliseconds (default: 10 minutes) + int idleTimeMillis = 600000; + + AtConnection() { + metaData.created = DateTime.now().toUtc(); + } + + /// Writes data to the underlying socket of the connection. /// @param - data - Data to write to the socket /// @throws [AtIOException] for any exception during the operation FutureOr write(String data); - /// closes the underlying connection + /// Closes the underlying connection. Future close(); - /// Returns true if the connection is invalid - bool isInValid(); + /// Returns true if the connection is invalid. + bool isInValid() { + return _isIdle() || metaData.isClosed || metaData.isStale; + } + + /// Updates the idle time for the connection (Socket or WebSocket). + void setIdleTime(int? idleTimeMillis) { + if (idleTimeMillis != null) { + this.idleTimeMillis = idleTimeMillis; + } + } + + /// Checks if the connection has been idle for longer than the specified timeout. + bool _isIdle() { + return _getIdleTimeMillis() > idleTimeMillis; + } - /// Gets the connection metadata - AtConnectionMetaData? getMetaData(); + /// Calculates the idle time in milliseconds. + int _getIdleTimeMillis() { + var lastAccessedTime = metaData.lastAccessed; + lastAccessedTime ??= metaData.created; + var currentTime = DateTime.now().toUtc(); + return currentTime.difference(lastAccessedTime!).inMilliseconds; + } } -abstract class AtConnectionMetaData { +/// Metadata for [AtConnection]. +class AtConnectionMetaData { bool isAuthenticated = false; DateTime? lastAccessed; DateTime? created; diff --git a/packages/at_lookup/lib/src/connection/at_lookup_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_lookup_connection_factory.dart new file mode 100644 index 00000000..bdf87b67 --- /dev/null +++ b/packages/at_lookup/lib/src/connection/at_lookup_connection_factory.dart @@ -0,0 +1,79 @@ +import 'dart:io'; + +import 'package:at_commons/at_commons.dart'; +import 'package:at_lookup/at_lookup.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; +import 'package:at_lookup/src/connection/at_socket_connection.dart'; +import 'package:at_lookup/src/connection/at_websocket_connection.dart'; + +import 'at_message_listener.dart'; + +/// This factory is responsible for creating the underlying connection, +/// an connection wrapper, and the message listener for a +/// specific type of connection (e.g., `SecureSocket` or `WebSocket`). +abstract class AtLookupConnectionFactory { + /// Creates the underlying connection of type [T]. + Future createUnderlying( + String host, String port, SecureSocketConfig secureSocketConfig); + + /// Wraps the underlying connection of type [T] into an connection [U]. + U createConnection(T underlying); + + /// Creates an [AtMessageListener] to manage messages for the given [U] connection. + AtMessageListener createListener(U connection); +} + +/// Factory class to create a secure connection over [SecureSocket]. +class AtLookupSecureSocketFactory extends AtLookupConnectionFactory< + SecureSocket, AtConnection> { + /// Creates a secure socket connection to the specified [host] and [port] + /// using the given [secureSocketConfig]. Returns a [SecureSocket] + @override + Future createUnderlying( + String host, String port, SecureSocketConfig secureSocketConfig) async { + return await SecureSocketUtil.createSecureSocket( + host, port, secureSocketConfig); + } + + /// Wraps the [SecureSocket] connection into an [AtConnection] instance. + @override + AtConnection createConnection(SecureSocket underlying) { + return AtSocketConnection(underlying); + } + + /// Creates an [AtMessageListener] to manage messages for the secure + /// socket-based [AtConnection]. + @override + AtMessageListener createListener(AtConnection connection) { + return AtMessageListener(connection); + } +} + +/// Factory class to create a WebSocket-based connection. +class AtLookupWebSocketFactory extends AtLookupConnectionFactory< + WebSocket, AtConnection> { + /// Creates a WebSocket connection to the specified [host] and [port] + /// using the given [secureSocketConfig]. + @override + Future createUnderlying( + String host, String port, SecureSocketConfig secureSocketConfig) async { + final socket = await SecureSocketUtil.createSecureSocket( + host, port, secureSocketConfig, + isWebSocket: true); + return socket as WebSocket; + } + + /// Wraps the [WebSocket] connection into an [AtConnection] instance. + @override + AtConnection createConnection(underlying) { + return AtWebSocketConnection(underlying); + } + + /// Creates an [AtMessageListener] to manage messages for the + /// WebSocket-based [AtConnection]. + @override + AtMessageListener createListener( + AtConnection connection) { + return AtMessageListener(connection); + } +} diff --git a/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart b/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart deleted file mode 100644 index 68dcaea4..00000000 --- a/packages/at_lookup/lib/src/connection/at_lookup_outbound_connection_factory.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'dart:io'; - -import 'package:at_commons/at_commons.dart'; -import 'package:at_lookup/at_lookup.dart'; -import 'package:at_lookup/src/connection/outbound_websocket_connection_impl.dart'; - -import 'outbound_message_listener.dart'; - -/// This factory is responsible for creating the underlying connection, -/// an outbound connection wrapper, and the message listener for a -/// specific type of connection (e.g., `SecureSocket` or `WebSocket`). -abstract class AtLookupOutboundConnectionFactory { - /// Creates the underlying connection of type [T]. - Future createUnderlying( - String host, String port, SecureSocketConfig secureSocketConfig); - - /// Wraps the underlying connection of type [T] into an outbound connection [U]. - U createConnection(T underlying); - - /// Creates an [OutboundMessageListener] to manage messages for the given [U] connection. - OutboundMessageListener createListener(U connection); -} - -/// Factory class to create a secure outbound connection over [SecureSocket]. -class AtLookupSecureSocketFactory extends AtLookupOutboundConnectionFactory< - SecureSocket, OutboundConnection> { - /// Creates a secure socket connection to the specified [host] and [port] - /// using the given [secureSocketConfig]. Returns a [SecureSocket] - @override - Future createUnderlying( - String host, String port, SecureSocketConfig secureSocketConfig) async { - return await SecureSocketUtil.createSecureSocket( - host, port, secureSocketConfig); - } - - /// Wraps the [SecureSocket] connection into an [OutboundConnection] instance. - @override - OutboundConnection createConnection(SecureSocket underlying) { - return OutboundConnectionImpl(underlying); - } - - /// Creates an [OutboundMessageListener] to manage messages for the secure - /// socket-based [OutboundConnection]. - @override - OutboundMessageListener createListener(OutboundConnection connection) { - return OutboundMessageListener(connection); - } -} - -/// Factory class to create a WebSocket-based outbound connection. -class AtLookupWebSocketFactory extends AtLookupOutboundConnectionFactory< - WebSocket, OutboundWebSocketConnection> { - /// Creates a WebSocket connection to the specified [host] and [port] - /// using the given [secureSocketConfig]. - @override - Future createUnderlying( - String host, String port, SecureSocketConfig secureSocketConfig) async { - final socket = await SecureSocketUtil.createSecureSocket( - host, port, secureSocketConfig, - isWebSocket: true); - return socket as WebSocket; - } - - /// Wraps the [WebSocket] connection into an [OutboundWebSocketConnection] instance. - @override - OutboundWebSocketConnection createConnection(WebSocket underlying) { - return OutboundWebsocketConnectionImpl(underlying); - } - - /// Creates an [OutboundMessageListener] to manage messages for the - /// WebSocket-based [OutboundWebSocketConnection]. - @override - OutboundMessageListener createListener( - OutboundWebSocketConnection connection) { - return OutboundMessageListener(connection); - } -} diff --git a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart b/packages/at_lookup/lib/src/connection/at_message_listener.dart similarity index 98% rename from packages/at_lookup/lib/src/connection/outbound_message_listener.dart rename to packages/at_lookup/lib/src/connection/at_message_listener.dart index 468388f9..52813510 100644 --- a/packages/at_lookup/lib/src/connection/outbound_message_listener.dart +++ b/packages/at_lookup/lib/src/connection/at_message_listener.dart @@ -9,7 +9,7 @@ import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; /// Listener class for messages received by [RemoteSecondary] -class OutboundMessageListener { +class AtMessageListener { final logger = AtSignLogger('OutboundMessageListener'); late ByteBuffer _buffer; final Queue _queue = Queue(); @@ -19,7 +19,7 @@ class OutboundMessageListener { final int atCharCodeUnit = 64; late DateTime _lastReceivedTime; - OutboundMessageListener(this._connection, {int bufferCapacity = 10240000}) { + AtMessageListener(this._connection, {int bufferCapacity = 10240000}) { _buffer = ByteBuffer(capacity: bufferCapacity); } diff --git a/packages/at_lookup/lib/src/connection/base_connection.dart b/packages/at_lookup/lib/src/connection/at_socket_connection.dart similarity index 57% rename from packages/at_lookup/lib/src/connection/base_connection.dart rename to packages/at_lookup/lib/src/connection/at_socket_connection.dart index 442b09fd..9f6df5c4 100644 --- a/packages/at_lookup/lib/src/connection/base_connection.dart +++ b/packages/at_lookup/lib/src/connection/at_socket_connection.dart @@ -6,26 +6,19 @@ import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_utils/at_logger.dart'; /// Base class for common socket operations -abstract class BaseConnection extends AtConnection { +class AtSocketConnection extends AtConnection { final T _socket; - late final AtSignLogger logger; - StringBuffer? buffer; - AtConnectionMetaData? metaData; + final AtSignLogger logger = AtSignLogger('AtSocketConnection'); + final StringBuffer buffer = StringBuffer(); - BaseConnection(this._socket) { - logger = AtSignLogger(runtimeType.toString()); - buffer = StringBuffer(); + AtSocketConnection(this._socket) { _socket.setOption(SocketOption.tcpNoDelay, true); - } - - @override - AtConnectionMetaData? getMetaData() { - return metaData; + metaData.created = DateTime.now().toUtc(); } @override Future close() async { - if (getMetaData()!.isClosed) { + if (metaData.isClosed) { logger.finer('close(): connection is already closed'); return; } @@ -34,32 +27,31 @@ abstract class BaseConnection extends AtConnection { var address = underlying.remoteAddress; var port = underlying.remotePort; - logger.info('close(): calling socket.destroy()' - ' on connection to $address:$port'); - _socket.destroy(); + logger.info( + 'close(): calling socket.destroy() on connection to $address:$port'); + underlying.destroy(); } catch (e) { // Ignore errors or exceptions on a connection close logger.finer('Exception "$e" while destroying socket - ignoring'); - getMetaData()!.isStale = true; + metaData.isStale = true; } finally { - getMetaData()!.isClosed = true; + metaData.isClosed = true; } } - @override + @override T get underlying => _socket; @override FutureOr write(String data) async { if (isInValid()) { - //# Replace with specific exception throw ConnectionInvalidException('write(): Connection is invalid'); } try { underlying.write(data); - getMetaData()!.lastAccessed = DateTime.now().toUtc(); + metaData.lastAccessed = DateTime.now().toUtc(); } on Exception { - getMetaData()!.isStale = true; + metaData.isStale = true; } } } diff --git a/packages/at_lookup/lib/src/connection/base_websocket_connection.dart b/packages/at_lookup/lib/src/connection/at_websocket_connection.dart similarity index 68% rename from packages/at_lookup/lib/src/connection/base_websocket_connection.dart rename to packages/at_lookup/lib/src/connection/at_websocket_connection.dart index 9e4a8e64..7ba3170e 100644 --- a/packages/at_lookup/lib/src/connection/base_websocket_connection.dart +++ b/packages/at_lookup/lib/src/connection/at_websocket_connection.dart @@ -5,25 +5,20 @@ import 'package:at_lookup/src/connection/at_connection.dart'; import 'package:at_utils/at_logger.dart'; /// WebSocket-specific connection class -abstract class BaseWebSocketConnection extends AtConnection { - final WebSocket _webSocket; +class AtWebSocketConnection extends AtConnection { + final T _webSocket; late final AtSignLogger logger; StringBuffer? buffer; - AtConnectionMetaData? metaData; - BaseWebSocketConnection(this._webSocket) { + AtWebSocketConnection(this._webSocket) { logger = AtSignLogger(runtimeType.toString()); buffer = StringBuffer(); - } - - @override - AtConnectionMetaData? getMetaData() { - return metaData; + metaData.created = DateTime.now().toUtc(); } @override Future close() async { - if (getMetaData()!.isClosed) { + if (metaData.isClosed) { logger.finer('close(): WebSocket connection is already closed'); return; } @@ -34,14 +29,14 @@ abstract class BaseWebSocketConnection extends AtConnection { } catch (e) { // Ignore errors or exceptions on connection close logger.finer('Exception "$e" while closing WebSocket - ignoring'); - getMetaData()!.isStale = true; + metaData.isStale = true; } finally { - getMetaData()!.isClosed = true; + metaData.isClosed = true; } } @override - WebSocket get underlying => _webSocket; + T get underlying => _webSocket; @override FutureOr write(String data) async { @@ -52,10 +47,9 @@ abstract class BaseWebSocketConnection extends AtConnection { try { _webSocket.add(data); // WebSocket uses add() to send data - getMetaData()!.lastAccessed = DateTime.now().toUtc(); + metaData.lastAccessed = DateTime.now().toUtc(); } on Exception { - getMetaData()!.isStale = true; + metaData.isStale = true; } } - } diff --git a/packages/at_lookup/lib/src/connection/outbound_connection.dart b/packages/at_lookup/lib/src/connection/outbound_connection.dart deleted file mode 100644 index 3581a816..00000000 --- a/packages/at_lookup/lib/src/connection/outbound_connection.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'dart:io'; -import 'package:at_lookup/src/connection/at_connection.dart'; -import 'package:at_lookup/src/connection/base_connection.dart'; - -abstract class OutboundConnection extends BaseConnection { - OutboundConnection(T super.socket); - void setIdleTime(int? idleTimeMillis); -} - -/// Metadata information for [OutboundConnection] -class OutboundConnectionMetadata extends AtConnectionMetaData {} diff --git a/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart b/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart deleted file mode 100644 index 45f3666b..00000000 --- a/packages/at_lookup/lib/src/connection/outbound_connection_impl.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'dart:io'; -import 'outbound_connection.dart'; - -class OutboundConnectionImpl extends OutboundConnection { - int? outboundIdleTime = 600000; //default timeout 10 minutes - - OutboundConnectionImpl(T socket) : super(socket) { - metaData = OutboundConnectionMetadata()..created = DateTime.now().toUtc(); - } - - int _getIdleTimeMillis() { - var lastAccessedTime = getMetaData()!.lastAccessed; - lastAccessedTime ??= getMetaData()!.created; - var currentTime = DateTime.now().toUtc(); - return currentTime.difference(lastAccessedTime!).inMilliseconds; - } - - bool _isIdle() { - return _getIdleTimeMillis() > outboundIdleTime!; - } - - @override - bool isInValid() { - return _isIdle() || getMetaData()!.isClosed || getMetaData()!.isStale; - } - - @override - void setIdleTime(int? idleTimeMillis) { - outboundIdleTime = idleTimeMillis; - } -} diff --git a/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart b/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart deleted file mode 100644 index 1b6027c6..00000000 --- a/packages/at_lookup/lib/src/connection/outbound_websocket_connection.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'dart:io'; -import 'package:at_lookup/src/connection/base_websocket_connection.dart'; - - -abstract class OutboundWebSocketConnection extends BaseWebSocketConnection { - final WebSocket webSocket; - - OutboundWebSocketConnection(this.webSocket) : super(webSocket); - - void setIdleTime(int? idleTimeMillis); -} diff --git a/packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart b/packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart deleted file mode 100644 index 9f7279c1..00000000 --- a/packages/at_lookup/lib/src/connection/outbound_websocket_connection_impl.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'dart:io'; -import 'outbound_websocket_connection.dart'; -import 'outbound_connection.dart'; - -class OutboundWebsocketConnectionImpl - extends OutboundWebSocketConnection { - int? outboundIdleTime = 600000; //default timeout 10 minutes - - OutboundWebsocketConnectionImpl(T websocket) : super(websocket) { - metaData = OutboundConnectionMetadata()..created = DateTime.now().toUtc(); - } - - int _getIdleTimeMillis() { - var lastAccessedTime = getMetaData()!.lastAccessed; - lastAccessedTime ??= getMetaData()!.created; - var currentTime = DateTime.now().toUtc(); - return currentTime.difference(lastAccessedTime!).inMilliseconds; - } - - bool _isIdle() { - return _getIdleTimeMillis() > outboundIdleTime!; - } - - @override - bool isInValid() { - return _isIdle() || getMetaData()!.isClosed || getMetaData()!.isStale; - } - - @override - void setIdleTime(int? idleTimeMillis) { - outboundIdleTime = idleTimeMillis; - } -} diff --git a/packages/at_lookup/lib/src/monitor_client.dart b/packages/at_lookup/lib/src/monitor_client.dart index 1c14dea4..1a8bd338 100644 --- a/packages/at_lookup/lib/src/monitor_client.dart +++ b/packages/at_lookup/lib/src/monitor_client.dart @@ -6,6 +6,8 @@ import 'dart:typed_data'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; +import 'package:at_lookup/src/connection/at_socket_connection.dart'; import 'package:at_utils/at_logger.dart'; import 'package:crypton/crypton.dart'; @@ -21,7 +23,7 @@ class MonitorClient { } ///Monitor Verb - Future executeMonitorVerb(String _command, String _atSign, + Future executeMonitorVerb(String _command, String _atSign, String _rootDomain, int _rootPort, Function notificationCallBack, {bool auth = true, Function? restartCallBack}) async { //1. Get a new outbound connection dedicated to monitor verb. @@ -49,7 +51,7 @@ class MonitorClient { } /// Create a new connection for monitor verb. - Future _createNewConnection( + Future _createNewConnection( String toAtSign, String rootDomain, int rootPort) async { //1. find secondary url for atsign from lookup library var secondaryUrl = @@ -61,14 +63,14 @@ class MonitorClient { //2. create a connection to secondary server var secureSocket = await SecureSocket.connect(host, int.parse(port)); - OutboundConnection _monitorConnection = - OutboundConnectionImpl(secureSocket); + AtConnection _monitorConnection = + AtSocketConnection(secureSocket); return _monitorConnection; } /// To authenticate connection via PKAM verb. - Future _authenticateConnection( - String _atSign, OutboundConnection _monitorConnection) async { + Future _authenticateConnection( + String _atSign, AtConnection _monitorConnection) async { await _monitorConnection.write('from:$_atSign\n'); var fromResponse = await _getQueueResponse(); logger.info('from result:$fromResponse'); @@ -123,16 +125,16 @@ class MonitorClient { } /// Logs the error and closes the [OutboundConnection] - Future _errorHandler(error, OutboundConnection _connection) async { + Future _errorHandler(error, AtConnection _connection) async { await _closeConnection(_connection); } - /// Closes the [OutboundConnection] - void _finishedHandler(OutboundConnection _connection) async { + /// Closes the [AtConnection] + void _finishedHandler(AtConnection _connection) async { await _closeConnection(_connection); } - Future _closeConnection(OutboundConnection _connection) async { + Future _closeConnection(AtConnection _connection) async { if (!_connection.isInValid()) { await _connection.close(); } diff --git a/packages/at_lookup/test/at_lookup_test.dart b/packages/at_lookup/test/at_lookup_test.dart index 385f5c13..462fdf63 100644 --- a/packages/at_lookup/test/at_lookup_test.dart +++ b/packages/at_lookup/test/at_lookup_test.dart @@ -6,10 +6,10 @@ import 'package:at_commons/at_builders.dart'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; import 'package:at_lookup/src/connection/at_connection.dart'; -import 'package:at_lookup/src/connection/outbound_message_listener.dart'; -import 'package:test/test.dart'; -import 'package:mocktail/mocktail.dart'; +import 'package:at_lookup/src/connection/at_message_listener.dart'; import 'package:at_utils/at_logger.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; import 'at_lookup_test_utils.dart'; @@ -17,11 +17,11 @@ class FakeAtSigningInput extends Fake implements AtSigningInput {} void main() { AtSignLogger.root_level = 'finest'; - late OutboundConnection mockOutBoundConnection; - late OutboundWebSocketConnection mockOutboundWebsocketConnection; + late AtConnection mockAtConnection; + late SecondaryAddressFinder mockSecondaryAddressFinder; - late OutboundMessageListener mockOutboundListener; - late AtLookupOutboundConnectionFactory mockAtConnectionFactory; + late AtMessageListener mockAtMessageListener; + late AtLookupConnectionFactory mockAtConnectionFactory; late AtChops mockAtChops; late SecureSocket mockSecureSocket; @@ -32,11 +32,12 @@ void main() { group('A group of secure socket tests', () { setUp(() { - mockOutBoundConnection = MockOutboundConnectionImpl(); + mockAtConnection = MockAtSocketConnection(); mockSecondaryAddressFinder = MockSecondaryAddressFinder(); - mockOutboundListener = MockOutboundMessageListener(); - mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); + mockAtMessageListener = MockAtMessageListener(); + mockAtConnectionFactory = MockAtLookupConnectionFactory(); mockAtChops = MockAtChops(); + registerFallbackValue(SecureSocketConfig()); mockSecureSocket = createMockAtServerSocket(atServerHost, atServerPort); @@ -51,13 +52,13 @@ void main() { }); when(() => mockAtConnectionFactory.createConnection(mockSecureSocket)) - .thenAnswer((_) => mockOutBoundConnection); + .thenAnswer((_) => mockAtConnection); - when(() => mockOutBoundConnection.write(any())) + when(() => mockAtConnection.write(any())) .thenAnswer((_) => Future.value()); - when(() => mockAtConnectionFactory.createListener(mockOutBoundConnection)) - .thenAnswer((_) => mockOutboundListener); + when(() => mockAtConnectionFactory.createListener(mockAtConnection)) + .thenAnswer((_) => mockAtMessageListener); }); group('A group of tests to verify atlookup pkam authentication', () { @@ -73,14 +74,14 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value('data:success')); - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); - when(() => mockOutBoundConnection.write( + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) .thenAnswer((invocation) { mockSecureSocket.write( @@ -91,7 +92,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate(); expect(result, true); @@ -109,14 +110,14 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()).thenAnswer((_) => + when(() => mockAtMessageListener.read()).thenAnswer((_) => Future.value('error:AT0401-Exception: pkam authentication failed')); - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); - when(() => mockOutBoundConnection.write( + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) .thenAnswer((invocation) { mockSecureSocket.write( @@ -127,7 +128,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect(() async => await atLookup.pkamAuthenticate(), throwsA(predicate((e) => e is UnAuthenticatedException))); @@ -145,14 +146,14 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value('data:success')); - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); - when(() => mockOutBoundConnection.write( + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) .thenAnswer((invocation) { mockSecureSocket.write( @@ -162,7 +163,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate( enrollmentId: enrollmentIdFromServer); @@ -181,13 +182,13 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()).thenAnswer((_) => + when(() => mockAtMessageListener.read()).thenAnswer((_) => Future.value('error:AT0401-Exception: pkam authentication failed')); - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); - when(() => mockOutBoundConnection.write( + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) .thenAnswer((invocation) { mockSecureSocket.write( @@ -197,7 +198,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect( () async => await atLookup.pkamAuthenticate( @@ -212,10 +213,10 @@ void main() { test('executeCommand - from verb - auth false', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; final fromResponse = 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(fromResponse)); var result = await atLookup.executeCommand('from:@alice\n'); expect(result, fromResponse); @@ -226,7 +227,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); final fromResponse = 'data:1234'; - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(fromResponse)); expect( () async => await atLookup.executeCommand('llookup:phone@alice\n', @@ -237,16 +238,16 @@ void main() { test('executeCommand -llookup verb - auth true - at_chops set', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'data:1234'; - when(() => mockOutBoundConnection.write(llookupCommand)) + when(() => mockAtConnection.write(llookupCommand)) .thenAnswer((invocation) { mockSecureSocket.write(llookupCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(llookupResponse)); var result = await atLookup.executeCommand(llookupCommand); expect(result, llookupResponse); @@ -255,16 +256,16 @@ void main() { test('executeCommand - test non json error handling', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'error:AT0015-Exception: fubar'; - when(() => mockOutBoundConnection.write(llookupCommand)) + when(() => mockAtConnection.write(llookupCommand)) .thenAnswer((invocation) { mockSecureSocket.write(llookupCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(llookupResponse)); await expectLater( atLookup.executeCommand(llookupCommand), @@ -276,17 +277,17 @@ void main() { test('executeCommand - test json error handling', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; final llookupCommand = 'llookup:phone@alice\n'; final llookupResponse = 'error:{"errorCode":"AT0015","errorDescription":"Exception: fubar"}'; - when(() => mockOutBoundConnection.write(llookupCommand)) + when(() => mockAtConnection.write(llookupCommand)) .thenAnswer((invocation) { mockSecureSocket.write(llookupCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(llookupResponse)); await expectLater( atLookup.executeCommand(llookupCommand), @@ -300,7 +301,7 @@ void main() { test('validate EnrollVerbHandler behaviour - request', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_1'; String deviceName = 'test_device'; String otp = 'ABCDEF'; @@ -315,18 +316,17 @@ void main() { final enrollResponse = 'data:{"enrollmentId":"1234567890","status":"pending"}'; - when(() => mockOutBoundConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockSecureSocket.write(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = false; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = false; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); var result = await atLookup.executeVerb(enrollVerbBuilder); expect(result, enrollResponse); @@ -336,7 +336,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); atLookup.atChops = mockAtChops; - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_2'; String deviceName = 'test_device'; String enrollmentId = '1357913579'; @@ -351,18 +351,17 @@ void main() { final enrollResponse = 'data:{"enrollmentId":"1357913579","status":"approved"}'; - when(() => mockOutBoundConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockSecureSocket.write(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = true; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = true; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); @@ -371,7 +370,7 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); atLookup.atChops = mockAtChops; - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; String enrollmentId = '89213647826348'; EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() @@ -382,18 +381,17 @@ void main() { String enrollResponse = 'data:{"enrollmentId":"$enrollmentId","status":"revoked"}'; - when(() => mockOutBoundConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockSecureSocket.write(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = true; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = true; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); @@ -401,7 +399,7 @@ void main() { test('validate behaviour with EnrollVerbHandler - deny', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; String enrollmentId = '5754765754'; @@ -412,18 +410,17 @@ void main() { String enrollResponse = 'data:{"enrollmentId":"$enrollmentId","status":"denied"}'; - when(() => mockOutBoundConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockSecureSocket.write(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = true; - when(() => mockOutBoundConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutBoundConnection.isInValid()).thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = true; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); @@ -432,10 +429,10 @@ void main() { group('A group of web socket tests', () { setUp(() { - mockOutboundWebsocketConnection = MockOutboundWebsocketConnectionImpl(); + mockAtConnection = MockAtSocketConnection(); mockSecondaryAddressFinder = MockSecondaryAddressFinder(); - mockOutboundListener = MockOutboundMessageListener(); - mockAtConnectionFactory = MockAtLookupOutboundConnectionFactory(); + mockAtMessageListener = MockAtMessageListener(); + mockAtConnectionFactory = MockAtLookupConnectionFactory(); mockAtChops = MockAtChops(); registerFallbackValue(SecureSocketConfig()); mockWebSocket = createMockWebSocket(atServerHost, atServerPort); @@ -451,14 +448,13 @@ void main() { }); when(() => mockAtConnectionFactory.createConnection(mockWebSocket)) - .thenAnswer((_) => mockOutboundWebsocketConnection); + .thenAnswer((_) => mockAtConnection); - when(() => mockOutboundWebsocketConnection.write(any())) + when(() => mockAtConnection.write(any())) .thenAnswer((_) => Future.value()); - when(() => mockAtConnectionFactory - .createListener(mockOutboundWebsocketConnection)) - .thenAnswer((_) => mockOutboundListener); + when(() => mockAtConnectionFactory.createListener(mockAtConnection)) + .thenAnswer((_) => mockAtMessageListener); }); group('A group of tests to verify atlookup pkam authentication', () { @@ -475,15 +471,14 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value('data:success')); - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); - when(() => mockOutboundWebsocketConnection.write( + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) .thenAnswer((invocation) { mockWebSocket.add( @@ -493,9 +488,9 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate(); expect(result, true); @@ -514,15 +509,14 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()).thenAnswer((_) => + when(() => mockAtMessageListener.read()).thenAnswer((_) => Future.value('error:AT0401-Exception: pkam authentication failed')); - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); - when(() => mockOutboundWebsocketConnection.write( + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:$pkamSignature\n')) .thenAnswer((invocation) { mockWebSocket.add( @@ -532,9 +526,9 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; expect(() async => await atLookup.pkamAuthenticate(), throwsA(predicate((e) => e is UnAuthenticatedException))); @@ -553,15 +547,14 @@ void main() { when(() => mockAtChops.sign(any())) .thenReturn(AtSigningResult()..result = pkamSignature); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value('data:success')); - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(OutboundConnectionMetadata()..isAuthenticated = false); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + when(() => mockAtConnection.metaData) + .thenReturn(AtConnectionMetaData()..isAuthenticated = false); + when(() => mockAtConnection.isInValid()).thenReturn(false); - when(() => mockOutboundWebsocketConnection.write( + when(() => mockAtConnection.write( 'pkam:signingAlgo:rsa2048:hashingAlgo:sha256:enrollmentId:$enrollmentIdFromServer:$pkamSignature\n')) .thenAnswer((invocation) { mockWebSocket.add( @@ -571,8 +564,8 @@ void main() { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; var result = await atLookup.pkamAuthenticate( enrollmentId: enrollmentIdFromServer); @@ -584,11 +577,11 @@ void main() { test('executeCommand using websocket- from verb - auth false', () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atConnectionFactory = mockAtConnectionFactory; final fromResponse = 'data:_03fe0ff2-ac50-4c80-8f43-88480beba888@alice:c3d345fc-5691-4f90-bc34-17cba31f060f'; - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(fromResponse)); var result = await atLookup.executeCommand('from:@alice\n'); expect(result, fromResponse); @@ -599,9 +592,9 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); final fromResponse = 'data:1234'; - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(fromResponse)); expect( () async => await atLookup.executeCommand('llookup:phone@alice\n', @@ -616,8 +609,8 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_1'; String deviceName = 'test_device'; String otp = 'ABCDEF'; @@ -632,19 +625,17 @@ void main() { final enrollResponse = 'data:{"enrollmentId":"1234567890","status":"pending"}'; - when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockWebSocket.add(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = false; - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = false; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); var result = await atLookup.executeVerb(enrollVerbBuilder); expect(result, enrollResponse); @@ -655,9 +646,9 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = mockAtChops; - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; String appName = 'unit_test_2'; String deviceName = 'test_device'; String enrollmentId = '1357913579'; @@ -672,19 +663,17 @@ void main() { final enrollResponse = 'data:{"enrollmentId":"1357913579","status":"approved"}'; - when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockWebSocket.add(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = true; - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = true; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); @@ -694,9 +683,9 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = mockAtChops; - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; String enrollmentId = '89213647826348'; EnrollVerbBuilder enrollVerbBuilder = EnrollVerbBuilder() @@ -707,19 +696,17 @@ void main() { String enrollResponse = 'data:{"enrollmentId":"$enrollmentId","status":"revoked"}'; - when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockWebSocket.add(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = true; - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = true; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); @@ -729,8 +716,8 @@ void main() { () async { final atLookup = AtLookupImpl('@alice', atServerHost, 64, secondaryAddressFinder: mockSecondaryAddressFinder, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); - atLookup.atOutboundConnectionFactory = mockAtConnectionFactory; + atConnectionFactory: AtLookupWebSocketFactory()); + atLookup.atConnectionFactory = mockAtConnectionFactory; atLookup.atChops = mockAtChops; String enrollmentId = '5754765754'; @@ -741,19 +728,17 @@ void main() { String enrollResponse = 'data:{"enrollmentId":"$enrollmentId","status":"denied"}'; - when(() => mockOutboundWebsocketConnection.write(enrollCommand)) + when(() => mockAtConnection.write(enrollCommand)) .thenAnswer((invocation) { mockWebSocket.add(enrollCommand); return Future.value(); }); - when(() => mockOutboundListener.read()) + when(() => mockAtMessageListener.read()) .thenAnswer((_) => Future.value(enrollResponse)); - AtConnectionMetaData? atConnectionMetaData = - OutboundConnectionMetadata()..isAuthenticated = true; - when(() => mockOutboundWebsocketConnection.getMetaData()) - .thenReturn(atConnectionMetaData); - when(() => mockOutboundWebsocketConnection.isInValid()) - .thenReturn(false); + AtConnectionMetaData? atConnectionMetaData = AtConnectionMetaData() + ..isAuthenticated = true; + when(() => mockAtConnection.metaData).thenReturn(atConnectionMetaData); + when(() => mockAtConnection.isInValid()).thenReturn(false); expect(await atLookup.executeVerb(enrollVerbBuilder), enrollResponse); }); diff --git a/packages/at_lookup/test/at_lookup_test_utils.dart b/packages/at_lookup/test/at_lookup_test_utils.dart index 8733ee35..43474842 100644 --- a/packages/at_lookup/test/at_lookup_test_utils.dart +++ b/packages/at_lookup/test/at_lookup_test_utils.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'package:at_chops/at_chops.dart'; import 'package:at_lookup/at_lookup.dart'; -import 'package:at_lookup/src/connection/outbound_message_listener.dart'; +import 'package:at_lookup/src/connection/at_message_listener.dart'; +import 'package:at_lookup/src/connection/at_socket_connection.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:at_lookup/src/connection/outbound_websocket_connection_impl.dart'; int mockSocketNumber = 1; @@ -14,8 +14,8 @@ class MockSecondaryAddressFinder extends Mock class MockSecondaryUrlFinder extends Mock implements SecondaryUrlFinder {} -class MockAtLookupOutboundConnectionFactory extends Mock - implements AtLookupOutboundConnectionFactory {} +class MockAtLookupConnectionFactory extends Mock + implements AtLookupConnectionFactory {} class MockStreamSubscription extends Mock implements StreamSubscription {} @@ -29,16 +29,11 @@ class MockWebSocket extends Mock implements WebSocket { int mockNumber = mockSocketNumber++; } -class MockOutboundMessageListener extends Mock - implements OutboundMessageListener {} +class MockAtMessageListener extends Mock implements AtMessageListener {} class MockAtChops extends Mock implements AtChopsImpl {} -class MockOutboundConnectionImpl extends Mock - implements OutboundConnectionImpl {} - -class MockOutboundWebsocketConnectionImpl extends Mock - implements OutboundWebsocketConnectionImpl {} +class MockAtSocketConnection extends Mock implements AtSocketConnection {} SecureSocket createMockAtServerSocket(String address, int port) { SecureSocket mss = MockSecureSocket(); diff --git a/packages/at_lookup/test/connection_management_test.dart b/packages/at_lookup/test/connection_management_test.dart index 0c7d4fca..49493c4f 100644 --- a/packages/at_lookup/test/connection_management_test.dart +++ b/packages/at_lookup/test/connection_management_test.dart @@ -3,23 +3,22 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; -import 'package:at_lookup/src/connection/outbound_message_listener.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; +import 'package:at_lookup/src/connection/at_message_listener.dart'; +import 'package:at_lookup/src/connection/at_socket_connection.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; import 'at_lookup_test_utils.dart'; -class MockOutboundConnectionImpl extends Mock - implements OutboundConnectionImpl {} - class MockAtLookupSecureSocketFactory extends Mock implements AtLookupSecureSocketFactory {} void main() { group('test connection close and socket cleanup', () { late SecondaryAddressFinder finder; - late MockAtLookupOutboundConnectionFactory mockOutboundConnectionFactory; - late OutboundMessageListener mockOutboundListener; + late MockAtLookupConnectionFactory mockAtConnectionFactory; + late AtMessageListener mockAtMessageListener; setUp(() { mockSocketNumber = 1; // Reset counter for each test @@ -27,26 +26,25 @@ void main() { when(() => finder.findSecondary(any())) .thenAnswer((_) async => SecondaryAddress('test.test.test', 12345)); - mockOutboundConnectionFactory = MockAtLookupOutboundConnectionFactory(); + mockAtConnectionFactory = MockAtLookupConnectionFactory(); registerFallbackValue(SecureSocketConfig()); - mockOutboundListener = MockOutboundMessageListener(); + mockAtMessageListener = MockAtMessageListener(); - when(() => mockOutboundConnectionFactory.createUnderlying( + when(() => mockAtConnectionFactory.createUnderlying( 'test.test.test', '12345', any())).thenAnswer((_) { // Each call provides a new instance of MockSecureSocket SecureSocket newMockSocket = createMockAtServerSocket('test.test.test', 12345); - OutboundConnectionImpl newOutboundConnectionImpl = - OutboundConnectionImpl(newMockSocket); + AtSocketConnection atConnection = AtSocketConnection(newMockSocket); // Update factory to return this new connection and listener when(() => - mockOutboundConnectionFactory.createConnection(newMockSocket)) - .thenReturn(newOutboundConnectionImpl); + mockAtConnectionFactory.createConnection(newMockSocket)) + .thenReturn(atConnection); - when(() => mockOutboundConnectionFactory.createListener( - newOutboundConnectionImpl)).thenAnswer((_) => mockOutboundListener); + when(() => mockAtConnectionFactory.createListener(atConnection)) + .thenAnswer((_) => mockAtMessageListener); return Future.value(newMockSocket); }); }); @@ -57,7 +55,7 @@ void main() { AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, secondaryAddressFinder: finder); - expect(atLookup.atOutboundConnectionFactory.runtimeType.toString(), + expect(atLookup.atConnectionFactory.runtimeType.toString(), "AtLookupSecureSocketFactory"); expect(() async => await atLookup.createConnection(), throwsA(predicate((dynamic e) => e is SecondaryConnectException))); @@ -80,7 +78,7 @@ void main() { SecureSocketConfig secureSocketConfig = SecureSocketConfig(); // Setting mock instances - atLookup.atOutboundConnectionFactory = mockAtLookupSecureSocketFactory; + atLookup.atConnectionFactory = mockAtLookupSecureSocketFactory; // Set mock responses. when(() => mockAtLookupSecureSocketFactory.createUnderlying( @@ -90,25 +88,23 @@ void main() { when(() => mockSecureSocket.setOption(SocketOption.tcpNoDelay, true)) .thenReturn(true); - // In the constructor of [BaseConnection] which is super class of [OutboundConnectionImpl] - // socket.setOption is invoked. Therefore, the initialization of outboundConnectionImpl + // In the constructor of [AtSocketConnection] which is super class of AtConnection + // socket.setOption is invoked. Therefore, the initialization of AtSocketConnection // should be executed after when(() => mockSecureSocket.setOption). // Otherwise a null exception arises. - OutboundConnectionImpl outboundConnectionImpl = - OutboundConnectionImpl(mockSecureSocket); - - when(() => mockAtLookupSecureSocketFactory.createConnection( - mockSecureSocket)).thenReturn(outboundConnectionImpl); + AtSocketConnection atConnection = AtSocketConnection(mockSecureSocket); when(() => mockAtLookupSecureSocketFactory - .createListener(outboundConnectionImpl)) - .thenReturn(OutboundMessageListener(outboundConnectionImpl)); + .createConnection(mockSecureSocket)).thenReturn(atConnection); + + when(() => mockAtLookupSecureSocketFactory.createListener(atConnection)) + .thenReturn(AtMessageListener(atConnection)); // Setting connection timeout to 2 seconds. - atLookup.outboundConnectionTimeout = Duration(seconds: 2).inMilliseconds; - // Create outbound connection. - bool isConnCreated = await atLookup.createOutboundConnection( - host, '$port', secureSocketConfig); + atLookup.atConnectionTimeout = Duration(seconds: 2).inMilliseconds; + // Create connection. + bool isConnCreated = + await atLookup.createAtConnection(host, '$port', secureSocketConfig); expect(isConnCreated, true); expect(atLookup.connection?.isInValid(), false); // Wait for the connection to timeout. @@ -121,23 +117,23 @@ void main() { () async { AtLookupImpl atLookup = AtLookupImpl('@alice', 'test.test.test', 64, secondaryAddressFinder: finder); - expect(atLookup.atOutboundConnectionFactory.runtimeType.toString(), + expect(atLookup.atConnectionFactory.runtimeType.toString(), "AtLookupSecureSocketFactory"); // Override atConnectionFactory with mock in AtLookupImpl - atLookup.atOutboundConnectionFactory = mockOutboundConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; await atLookup.createConnection(); // let's get a handle to the first socket & connection - OutboundConnection firstConnection = - atLookup.connection as OutboundConnection; // Explicit cast + AtSocketConnection firstConnection = + atLookup.connection as AtSocketConnection; // Explicit cast MockSecureSocket firstSocket = firstConnection.underlying as MockSecureSocket; expect(firstSocket.mockNumber, 1); expect(firstSocket.destroyed, false); - expect(firstConnection.metaData!.isClosed, false); + expect(firstConnection.metaData.isClosed, false); expect(firstConnection.isInValid(), false); // Make the connection appear 'idle' @@ -145,7 +141,7 @@ void main() { await Future.delayed(Duration(milliseconds: 2)); expect(firstConnection.isInValid(), true); - atLookup.atOutboundConnectionFactory = mockOutboundConnectionFactory; + atLookup.atConnectionFactory = mockAtConnectionFactory; // When we now call AtLookupImpl's createConnection again, it should: // - notice that its current connection is 'idle', and close it @@ -154,72 +150,72 @@ void main() { // has the first connection been closed, and its socket destroyed? expect(firstSocket.destroyed, true); - expect(firstConnection.metaData!.isClosed, true); + expect(firstConnection.metaData.isClosed, true); // has a new connection been created, with a new socket? - OutboundConnection secondConnection = - atLookup.connection as OutboundConnection; // Explicit cast + AtSocketConnection secondConnection = + atLookup.connection as AtSocketConnection; // Explicit cast MockSecureSocket secondSocket = secondConnection.underlying as MockSecureSocket; expect(firstConnection.hashCode == secondConnection.hashCode, false); expect(secondSocket.mockNumber, 2); expect(secondSocket.destroyed, false); - expect(secondConnection.metaData!.isClosed, false); + expect(secondConnection.metaData.isClosed, false); expect(secondConnection.isInValid(), false); }); test( 'test message listener closes connection' ' when socket listener onDone is called', () async { - OutboundConnection oc = OutboundConnectionImpl( - createMockAtServerSocket('test.test.test', 12345)); - OutboundMessageListener oml = OutboundMessageListener(oc); + AtConnection oc = + AtSocketConnection(createMockAtServerSocket('test.test.test', 12345)); + AtMessageListener oml = AtMessageListener(oc); expect((oc.underlying as MockSecureSocket).destroyed, false); - expect(oc.metaData?.isClosed, false); + expect(oc.metaData.isClosed, false); oml.onSocketDone(); expect((oc.underlying as MockSecureSocket).destroyed, true); - expect(oc.metaData?.isClosed, true); + expect(oc.metaData.isClosed, true); }); test( 'test message listener closes connection' ' when socket listener onError is called', () async { - OutboundConnection oc = OutboundConnectionImpl( - createMockAtServerSocket('test.test.test', 12345)); - OutboundMessageListener oml = OutboundMessageListener(oc); + AtConnection oc = + AtSocketConnection(createMockAtServerSocket('test.test.test', 12345)); + AtMessageListener oml = AtMessageListener(oc); expect((oc.underlying as MockSecureSocket).destroyed, false); - expect(oc.metaData?.isClosed, false); + expect(oc.metaData.isClosed, false); oml.onSocketError('test'); expect((oc.underlying as MockSecureSocket).destroyed, true); - expect(oc.metaData?.isClosed, true); + expect(oc.metaData.isClosed, true); }); test('test can safely call connection.close() repeatedly', () async { - OutboundConnection oc = OutboundConnectionImpl( - createMockAtServerSocket('test.test.test', 12345)); - OutboundMessageListener oml = OutboundMessageListener(oc); + AtConnection oc = + AtSocketConnection(createMockAtServerSocket('test.test.test', 12345)); + AtMessageListener oml = AtMessageListener(oc); expect((oc.underlying as MockSecureSocket).destroyed, false); - expect(oc.metaData?.isClosed, false); + expect(oc.metaData.isClosed, false); await oml.closeConnection(); expect((oc.underlying as MockSecureSocket).destroyed, true); - expect(oc.metaData?.isClosed, true); + expect(oc.metaData.isClosed, true); (oc.underlying as MockSecureSocket).destroyed = false; await oml.closeConnection(); // Since the connection was already closed above, // we don't expect destroy to be called on the socket again expect((oc.underlying as MockSecureSocket).destroyed, false); - expect(oc.metaData?.isClosed, true); + expect(oc.metaData.isClosed, true); }); test( - 'test that OutboundMessageListener.closeConnection will call' + 'test that AtMessageListener.closeConnection will call' ' connection.close if the connection is idle', () async { - OutboundConnection oc = OutboundConnectionImpl( - createMockAtServerSocket('test.test.test', 12345)); - OutboundMessageListener oml = OutboundMessageListener(oc); + AtConnection oc = + AtSocketConnection(createMockAtServerSocket('test.test.test', 12345)); + AtMessageListener oml = AtMessageListener(oc); expect((oc.underlying as MockSecureSocket).destroyed, false); - expect(oc.metaData?.isClosed, false); + expect(oc.metaData.isClosed, false); expect(oc.isInValid(), false); // Make the connection appear 'idle' @@ -230,17 +226,17 @@ void main() { await oml.closeConnection(); expect((oc.underlying as MockSecureSocket).destroyed, true); - expect(oc.metaData?.isClosed, true); + expect(oc.metaData.isClosed, true); }); test( - 'test that OutboundMessageListener.closeConnection will not call' + 'test that AtMessageListener.closeConnection will not call' ' connection.close if already marked closed', () async { - OutboundConnection oc = OutboundConnectionImpl( - createMockAtServerSocket('test.test.test', 12345)); - OutboundMessageListener oml = OutboundMessageListener(oc); + AtConnection oc = + AtSocketConnection(createMockAtServerSocket('test.test.test', 12345)); + AtMessageListener oml = AtMessageListener(oc); expect((oc.underlying as MockSecureSocket).destroyed, false); - oc.metaData!.isClosed = true; + oc.metaData.isClosed = true; await oml.closeConnection(); @@ -249,19 +245,19 @@ void main() { }); test( - 'test that OutboundMessageListener.closeConnection will call' + 'test that AtMessageListener.closeConnection will call' ' connection.close even if the connection is marked stale', () async { - OutboundConnection oc = OutboundConnectionImpl( - createMockAtServerSocket('test.test.test', 12345)); - OutboundMessageListener oml = OutboundMessageListener(oc); + AtConnection oc = + AtSocketConnection(createMockAtServerSocket('test.test.test', 12345)); + AtMessageListener oml = AtMessageListener(oc); expect((oc.underlying as MockSecureSocket).destroyed, false); - expect(oc.metaData?.isClosed, false); - oc.metaData!.isStale = true; + expect(oc.metaData.isClosed, false); + oc.metaData.isStale = true; await oml.closeConnection(); expect((oc.underlying as MockSecureSocket).destroyed, true); - expect(oc.metaData?.isClosed, true); + expect(oc.metaData.isClosed, true); }); }); @@ -270,9 +266,8 @@ void main() { Socket mockSocket = MockSecureSocket(); when(() => mockSocket.setOption(SocketOption.tcpNoDelay, true)) .thenAnswer((_) => true); - OutboundConnection connection = OutboundConnectionImpl(mockSocket); - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(connection); + AtConnection connection = AtSocketConnection(mockSocket); + AtMessageListener atMessageListener = AtMessageListener(connection); // We want to set up a connection, then call read() and have it time out. // When read() times out, the connection should be closed BEFORE the exception is thrown @@ -281,11 +276,11 @@ void main() { // This variable enables us to introduce a delay before closing the connection // The introduction of this delay enables the race condition (if it exists) to occur in this test if (delayBeforeClose != null) { - outboundMessageListener.delayBeforeClose = delayBeforeClose; + atMessageListener.delayBeforeClose = delayBeforeClose; } int transientWaitTimeMillis = 50; try { - await outboundMessageListener.read( + await atMessageListener.read( transientWaitTimeMillis: transientWaitTimeMillis); } on AtTimeoutException catch (expected) { expect( @@ -300,9 +295,8 @@ void main() { Socket mockSocket = MockSecureSocket(); when(() => mockSocket.setOption(SocketOption.tcpNoDelay, true)) .thenAnswer((_) => true); - OutboundConnection connection = OutboundConnectionImpl(mockSocket); - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(connection); + AtConnection connection = AtSocketConnection(mockSocket); + AtMessageListener atMessageListener = AtMessageListener(connection); // We want to set up a connection, then call read() and have it time out. // When read() times out, the connection should be closed BEFORE the exception is thrown @@ -311,12 +305,11 @@ void main() { // This variable enables us to introduce a delay before closing the connection // The introduction of this delay enables the race condition (if it exists) to occur in this test if (delayBeforeClose != null) { - outboundMessageListener.delayBeforeClose = delayBeforeClose; + atMessageListener.delayBeforeClose = delayBeforeClose; } int maxWaitMilliSeconds = 50; try { - await outboundMessageListener.read( - maxWaitMilliSeconds: maxWaitMilliSeconds); + await atMessageListener.read(maxWaitMilliSeconds: maxWaitMilliSeconds); expect(false, true, reason: 'Test should not have reached this point'); } on AtTimeoutException catch (expected) { expect(expected.message, @@ -329,9 +322,8 @@ void main() { Socket mockSocket = MockSecureSocket(); when(() => mockSocket.setOption(SocketOption.tcpNoDelay, true)) .thenAnswer((_) => true); - OutboundConnection connection = OutboundConnectionImpl(mockSocket); - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(connection); + AtConnection connection = AtSocketConnection(mockSocket); + AtMessageListener atMessageListener = AtMessageListener(connection); // We want to set up a connection, then call read() and have it time out. // When read() times out, the connection should be closed BEFORE the exception is thrown @@ -340,12 +332,11 @@ void main() { // This variable enables us to introduce a delay before closing the connection // The introduction of this delay enables the race condition (if it exists) to occur in this test if (delayBeforeClose != null) { - outboundMessageListener.delayBeforeClose = delayBeforeClose; + atMessageListener.delayBeforeClose = delayBeforeClose; } int maxWaitMilliSeconds = 50; try { - await outboundMessageListener.read( - maxWaitMilliSeconds: maxWaitMilliSeconds); + await atMessageListener.read(maxWaitMilliSeconds: maxWaitMilliSeconds); expect(false, true, reason: 'Test should not have reached this point'); } on AtTimeoutException catch (expected) { expect(expected.message, @@ -358,19 +349,19 @@ void main() { group('A group of tests to detect race condition in connection management', () { test( - 'Test that isInvalid is set on the OutboundConnection after transientWaitTime timeout BEFORE the OutboundMessageListener.read() returns', + 'Test that isInvalid is set on the AtConnection after transientWaitTime timeout BEFORE the AtMessageListener.read() returns', () async { await testOne(Duration(milliseconds: 100)); }); test( - 'Test that isInvalid is set on the OutboundConnection after maxWaitTime timeout BEFORE the OutboundMessageListener.read() returns', + 'Test that isInvalid is set on the AtConnection after maxWaitTime timeout BEFORE the AtMessageListener.read() returns', () async { await testTwo(Duration(milliseconds: 100)); }); test( - 'Test that an attempt to write to an outbound connection which has had a timeout will throw a ConnectionInvalidException', + 'Test that an attempt to write to an AtConnection which has had a timeout will throw a ConnectionInvalidException', () async { await testThree(Duration(milliseconds: 100)); }); @@ -378,22 +369,22 @@ void main() { /// These tests will pass even when the race condition exists because of complications in the event loop from testing /// The tests are here to verify that we haven't caused another problem from the introduction - /// of `@visibleForTesting Duration? delayBeforeClose` into OutboundMessageListener + /// of `@visibleForTesting Duration? delayBeforeClose` into AtMessageListener group('Same race condition tests without the artificial delay', () { test( - 'Test that isInvalid is set on the OutboundConnection after transientWaitTime timeout BEFORE the OutboundMessageListener.read() returns', + 'Test that isInvalid is set on the AtConnection after transientWaitTime timeout BEFORE the AtMessageListener.read() returns', () async { await testOne(null); }); test( - 'Test that isInvalid is set on the OutboundConnection after maxWaitTime timeout BEFORE the OutboundMessageListener.read() returns', + 'Test that isInvalid is set on the AtConnection after maxWaitTime timeout BEFORE the AtMessageListener.read() returns', () async { await testTwo(null); }); test( - 'Test that an attempt to write to an outbound connection which has had a timeout will throw a ConnectionInvalidException', + 'Test that an attempt to write to an AtConnection which has had a timeout will throw a ConnectionInvalidException', () async { await testThree(null); }); diff --git a/packages/at_lookup/test/outbound_message_listener_test.dart b/packages/at_lookup/test/outbound_message_listener_test.dart index 5c575414..46c7b6f8 100644 --- a/packages/at_lookup/test/outbound_message_listener_test.dart +++ b/packages/at_lookup/test/outbound_message_listener_test.dart @@ -1,73 +1,69 @@ import 'dart:async'; import 'package:at_commons/at_commons.dart'; -import 'package:at_lookup/at_lookup.dart'; -import 'package:at_lookup/src/connection/outbound_message_listener.dart'; -import 'package:test/test.dart'; +import 'package:at_lookup/src/connection/at_connection.dart'; +import 'package:at_lookup/src/connection/at_message_listener.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; import 'at_lookup_test_utils.dart'; void main() { - OutboundConnection mockOutBoundConnection = MockOutboundConnectionImpl(); + AtConnection mockAtConnection = MockAtSocketConnection(); - group('A group of tests to verify buffer of outbound message listener', () { - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(mockOutBoundConnection); + group('A group of tests to verify buffer of AtMessageListener', () { + AtMessageListener atMessageListener = AtMessageListener(mockAtConnection); test('A test to validate complete data comes in single packet', () async { - outboundMessageListener - .messageHandler('data:phone@alice\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:phone@alice\n@alice@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:phone@alice'); }); test( 'A test to validate complete data comes in packet and prompt in different packet', () async { - outboundMessageListener - .messageHandler('data:@bob:phone@alice\n'.codeUnits); - outboundMessageListener.messageHandler('@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:@bob:phone@alice\n'.codeUnits); + atMessageListener.messageHandler('@alice@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:@bob:phone@alice'); }); test('A test to validate data two complete data comes in single packets', () async { - outboundMessageListener + atMessageListener .messageHandler('data:@bob:phone@alice\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + var response = await atMessageListener.read(); expect(response, 'data:@bob:phone@alice'); - outboundMessageListener + atMessageListener .messageHandler('data:public:phone@alice\n@alice@'.codeUnits); - response = await outboundMessageListener.read(); + response = await atMessageListener.read(); expect(response, 'data:public:phone@alice'); }); test('A test to validate data two complete data comes in multiple packets', () async { - outboundMessageListener + atMessageListener .messageHandler('data:public:phone@alice\n@ali'.codeUnits); - outboundMessageListener.messageHandler('ce@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('ce@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:public:phone@alice'); - outboundMessageListener.messageHandler( + atMessageListener.messageHandler( 'data:@bob:location@alice,@bob:phone@alice\n@alice@'.codeUnits); - response = await outboundMessageListener.read(); + response = await atMessageListener.read(); expect(response, 'data:@bob:location@alice,@bob:phone@alice'); }); test('A test to validate single data comes two packets', () async { - outboundMessageListener - .messageHandler('data:public:phone@'.codeUnits); - outboundMessageListener.messageHandler('alice\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:public:phone@'.codeUnits); + atMessageListener.messageHandler('alice\n@alice@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:public:phone@alice'); }); test('A test to validate data contains @', () async { - outboundMessageListener + atMessageListener .messageHandler('data:phone@alice_12345675\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + var response = await atMessageListener.read(); expect(response, 'data:phone@alice_12345675'); }); @@ -75,71 +71,68 @@ void main() { 'A test to validate data contains @ and partial prompt of previous data', () async { // partial response of previous data. - outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); - outboundMessageListener.messageHandler('alice@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:hello\n@'.codeUnits); + atMessageListener.messageHandler('alice@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:hello'); - outboundMessageListener + atMessageListener .messageHandler('data:phone@alice_12345675\n@alice@'.codeUnits); - response = await outboundMessageListener.read(); + response = await atMessageListener.read(); expect(response, 'data:phone@alice_12345675'); }); test('A test to validate data contains new line character', () async { - outboundMessageListener.messageHandler( + atMessageListener.messageHandler( 'data:value_contains_\nin_the_value\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + var response = await atMessageListener.read(); expect(response, 'data:value_contains_\nin_the_value'); }); test('A test to validate data contains new line character and @', () async { - outboundMessageListener.messageHandler( + atMessageListener.messageHandler( 'data:the_key_is\n@bob:phone@alice\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + var response = await atMessageListener.read(); expect(response, 'data:the_key_is\n@bob:phone@alice'); }); }); group('A group of test to verify response from unauthorized connection', () { - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(mockOutBoundConnection); + AtMessageListener atMessageListener = AtMessageListener(mockAtConnection); test('A test to validate response from unauthorized connection', () async { - outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:hello\n@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:hello'); }); test('A test to validate multiple response from unauthorized connection', () async { - outboundMessageListener.messageHandler('data:hello\n@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:hello\n@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:hello'); - outboundMessageListener.messageHandler('data:hi\n@'.codeUnits); - response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:hi\n@'.codeUnits); + response = await atMessageListener.read(); expect(response, 'data:hi'); }); test( 'A test to validate response from unauthorized connection in multiple packets', () async { - outboundMessageListener - .messageHandler('data:public:location@alice,'.codeUnits); - outboundMessageListener - .messageHandler('public:phone@alice\n@'.codeUnits); - var response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:public:location@alice,'.codeUnits); + atMessageListener.messageHandler('public:phone@alice\n@'.codeUnits); + var response = await atMessageListener.read(); expect(response, 'data:public:location@alice,public:phone@alice'); - outboundMessageListener.messageHandler('data:hi\n@'.codeUnits); - response = await outboundMessageListener.read(); + atMessageListener.messageHandler('data:hi\n@'.codeUnits); + response = await atMessageListener.read(); expect(response, 'data:hi'); }); }); group('A group of test to validate buffer over flow scenarios', () { test('A test to verify buffer over flow exception', () { - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(mockOutBoundConnection, bufferCapacity: 10); + AtMessageListener atMessageListener = + AtMessageListener(mockAtConnection, bufferCapacity: 10); expect( - () => outboundMessageListener + () => atMessageListener .messageHandler('data:dummy_data_to_exceed_limit'.codeUnits), throwsA(predicate((dynamic e) => e is BufferOverFlowException && @@ -148,11 +141,11 @@ void main() { }); test('A test to verify buffer over flow with multiple data packets', () { - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(mockOutBoundConnection, bufferCapacity: 20); - outboundMessageListener.messageHandler('data:dummy_data'.codeUnits); + AtMessageListener atMessageListener = + AtMessageListener(mockAtConnection, bufferCapacity: 20); + atMessageListener.messageHandler('data:dummy_data'.codeUnits); expect( - () => outboundMessageListener + () => atMessageListener .messageHandler('to_exceed_limit\n@alice@'.codeUnits), throwsA(predicate((dynamic e) => e is BufferOverFlowException && @@ -163,37 +156,34 @@ void main() { group('A group of tests to verify error: and stream responses from server', () { - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(mockOutBoundConnection); + AtMessageListener atMessageListener = AtMessageListener(mockAtConnection); test('A test to validate complete error comes in single packet', () async { - outboundMessageListener.messageHandler( + atMessageListener.messageHandler( 'error:AT0012: Invalid value found\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + var response = await atMessageListener.read(); expect(response, 'error:AT0012: Invalid value found'); }); test('A test to validate complete error comes in single packet', () async { - outboundMessageListener + atMessageListener .messageHandler('stream:@bob:phone@alice\n@alice@'.codeUnits); - var response = await outboundMessageListener.read(); + var response = await atMessageListener.read(); expect(response, 'stream:@bob:phone@alice'); }); }); group('A group of tests to verify AtTimeOutException', () { - OutboundMessageListener outboundMessageListener = - OutboundMessageListener(mockOutBoundConnection); + AtMessageListener atMessageListener = AtMessageListener(mockAtConnection); setUp(() { - when(() => mockOutBoundConnection.isInValid()).thenAnswer((_) => false); - when(() => mockOutBoundConnection.close()) + when(() => mockAtConnection.isInValid()).thenAnswer((_) => false); + when(() => mockAtConnection.close()) .thenAnswer((Invocation invocation) async {}); }); test( 'A test to verify when no data is received from server within transientWaitTimeMillis', () async { expect( - () async => - await outboundMessageListener.read(transientWaitTimeMillis: 50), + () async => await atMessageListener.read(transientWaitTimeMillis: 50), throwsA(predicate((dynamic e) => e is AtTimeoutException && e.message @@ -205,7 +195,7 @@ void main() { expect( () async => // we want to trigger the maxWaitMilliSeconds exception, so setting transient to a higher value - await outboundMessageListener.read( + await atMessageListener.read( transientWaitTimeMillis: 100, maxWaitMilliSeconds: 50), throwsA(predicate((dynamic e) => e is AtTimeoutException && @@ -215,12 +205,10 @@ void main() { test( 'A test to verify partial response - wait time greater than transientWaitTimeMillis', () async { - outboundMessageListener - .messageHandler('data:public:phone@'.codeUnits); - outboundMessageListener.messageHandler('12'.codeUnits); + atMessageListener.messageHandler('data:public:phone@'.codeUnits); + atMessageListener.messageHandler('12'.codeUnits); expect( - () async => - await outboundMessageListener.read(transientWaitTimeMillis: 50), + () async => await atMessageListener.read(transientWaitTimeMillis: 50), throwsA(predicate((dynamic e) => e is AtTimeoutException && e.message @@ -229,16 +217,15 @@ void main() { test( 'A test to verify partial response - wait time greater than maxWaitMillis', () async { - outboundMessageListener - .messageHandler('data:public:phone@'.codeUnits); - outboundMessageListener.messageHandler('12'.codeUnits); - outboundMessageListener.messageHandler('34'.codeUnits); - outboundMessageListener.messageHandler('56'.codeUnits); - outboundMessageListener.messageHandler('78'.codeUnits); + atMessageListener.messageHandler('data:public:phone@'.codeUnits); + atMessageListener.messageHandler('12'.codeUnits); + atMessageListener.messageHandler('34'.codeUnits); + atMessageListener.messageHandler('56'.codeUnits); + atMessageListener.messageHandler('78'.codeUnits); expect( () async => // we want to trigger the maxWaitMilliSeconds exception, so setting transient to a higher value - await outboundMessageListener.read( + await atMessageListener.read( transientWaitTimeMillis: 30, maxWaitMilliSeconds: 20), throwsA(predicate((dynamic e) => e is AtTimeoutException && @@ -249,21 +236,21 @@ void main() { 'A test to verify full response received - delay between messages from server', () async { String? response; - unawaited(outboundMessageListener + unawaited(atMessageListener .read(transientWaitTimeMillis: 50) .whenComplete(() => {}) .then((value) => response = value)); - outboundMessageListener.messageHandler('data:'.codeUnits); + atMessageListener.messageHandler('data:'.codeUnits); await Future.delayed(Duration(milliseconds: 25)); - outboundMessageListener.messageHandler('12'.codeUnits); + atMessageListener.messageHandler('12'.codeUnits); await Future.delayed(Duration(milliseconds: 15)); - outboundMessageListener.messageHandler('34'.codeUnits); + atMessageListener.messageHandler('34'.codeUnits); await Future.delayed(Duration(milliseconds: 17)); - outboundMessageListener.messageHandler('56'.codeUnits); + atMessageListener.messageHandler('56'.codeUnits); await Future.delayed(Duration(milliseconds: 30)); - outboundMessageListener.messageHandler('78'.codeUnits); + atMessageListener.messageHandler('78'.codeUnits); await Future.delayed(Duration(milliseconds: 45)); - outboundMessageListener.messageHandler('910\n@'.codeUnits); + atMessageListener.messageHandler('910\n@'.codeUnits); await Future.delayed(Duration(milliseconds: 25)); expect(response, isNotEmpty); expect(response, 'data:12345678910'); @@ -272,24 +259,24 @@ void main() { 'A test to verify max wait timeout - delay between messages from server', () async { String? response; - await outboundMessageListener + await atMessageListener .read(maxWaitMilliSeconds: 100) .catchError((e) { return e.toString(); }) .whenComplete(() => {}) .then((value) => {response = value}); - outboundMessageListener.messageHandler('data:'.codeUnits); + atMessageListener.messageHandler('data:'.codeUnits); await Future.delayed(Duration(milliseconds: 15)); - outboundMessageListener.messageHandler('12'.codeUnits); + atMessageListener.messageHandler('12'.codeUnits); await Future.delayed(Duration(milliseconds: 10)); - outboundMessageListener.messageHandler('34'.codeUnits); + atMessageListener.messageHandler('34'.codeUnits); await Future.delayed(Duration(milliseconds: 12)); - outboundMessageListener.messageHandler('56'.codeUnits); + atMessageListener.messageHandler('56'.codeUnits); await Future.delayed(Duration(milliseconds: 13)); - outboundMessageListener.messageHandler('78'.codeUnits); + atMessageListener.messageHandler('78'.codeUnits); await Future.delayed(Duration(milliseconds: 20)); - outboundMessageListener.messageHandler('910'.codeUnits); + atMessageListener.messageHandler('910'.codeUnits); await Future.delayed(Duration(milliseconds: 50)); expect(response, isNotEmpty); expect( @@ -302,24 +289,24 @@ void main() { 'A test to verify transient timeout - delay between messages from server', () async { String? response; - await outboundMessageListener + await atMessageListener .read(transientWaitTimeMillis: 50) .catchError((e) { return e.toString(); }) .whenComplete(() => {}) .then((value) => {response = value}); - outboundMessageListener.messageHandler('data:'.codeUnits); + atMessageListener.messageHandler('data:'.codeUnits); await Future.delayed(Duration(milliseconds: 10)); - outboundMessageListener.messageHandler('12'.codeUnits); + atMessageListener.messageHandler('12'.codeUnits); await Future.delayed(Duration(milliseconds: 15)); - outboundMessageListener.messageHandler('34'.codeUnits); + atMessageListener.messageHandler('34'.codeUnits); await Future.delayed(Duration(milliseconds: 17)); - outboundMessageListener.messageHandler('56'.codeUnits); + atMessageListener.messageHandler('56'.codeUnits); await Future.delayed(Duration(milliseconds: 20)); - outboundMessageListener.messageHandler('78'.codeUnits); + atMessageListener.messageHandler('78'.codeUnits); await Future.delayed(Duration(milliseconds: 10)); - outboundMessageListener.messageHandler('910'.codeUnits); + atMessageListener.messageHandler('910'.codeUnits); await Future.delayed(Duration(milliseconds: 60)); expect(response, isNotEmpty); expect( diff --git a/packages/at_lookup/test/secondary_address_cache_test.dart b/packages/at_lookup/test/secondary_address_cache_test.dart index d6e1e0db..955cff15 100644 --- a/packages/at_lookup/test/secondary_address_cache_test.dart +++ b/packages/at_lookup/test/secondary_address_cache_test.dart @@ -2,9 +2,9 @@ import 'dart:io'; import 'package:at_commons/at_commons.dart'; import 'package:at_lookup/at_lookup.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:test/expect.dart'; import 'package:test/scaffolding.dart'; -import 'package:mocktail/mocktail.dart'; import 'at_lookup_test_utils.dart'; @@ -141,7 +141,7 @@ void main() async { late Function socketOnDataFn; late SecureSocket mockSocket; - late MockAtLookupOutboundConnectionFactory mockSocketFactory; + late MockAtLookupConnectionFactory mockSocketFactory; late CacheableSecondaryAddressFinder cachingAtServerFinder; @@ -162,7 +162,7 @@ void main() async { setUp(() { mockSocket = createMockAtDirectorySocket(mockAtDirectoryHost, 64); - mockSocketFactory = MockAtLookupOutboundConnectionFactory(); + mockSocketFactory = MockAtLookupConnectionFactory(); cachingAtServerFinder = CacheableSecondaryAddressFinder( mockAtDirectoryHost, 64, @@ -170,9 +170,8 @@ void main() async { socketFactory: mockSocketFactory)); numSocketCreateCalls = 0; - when(() => - mockSocketFactory.createUnderlying(mockAtDirectoryHost, '64', any())) - .thenAnswer((invocation) { + when(() => mockSocketFactory.createUnderlying( + mockAtDirectoryHost, '64', any())).thenAnswer((invocation) { print( 'mock create socket: numFailures $numSocketCreateCalls requiredFailures $requiredFailures'); if (numSocketCreateCalls++ < requiredFailures) { diff --git a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart index 128aceb0..10ae42b6 100644 --- a/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart +++ b/tests/at_onboarding_cli_functional_tests/test/at_lookup_test.dart @@ -1,9 +1,8 @@ +import 'package:at_chops/at_chops.dart'; import 'package:at_commons/at_builders.dart'; import 'package:at_commons/at_commons.dart'; import 'package:at_demo_data/at_demo_data.dart' as at_demos; import 'package:at_lookup/at_lookup.dart'; -import 'package:at_chops/at_chops.dart'; - import 'package:test/test.dart'; void main() { @@ -15,7 +14,7 @@ void main() { 'A test to verify a secure socket connection and do a cram authenticate and scan', () async { var atLookup = AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, - atOutboundConnectionFactory: AtLookupSecureSocketFactory()); + atConnectionFactory: AtLookupSecureSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -26,7 +25,7 @@ void main() { 'A test to verify a websocket connection and do a cram authenticate and scan', () async { var atLookup = AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -37,7 +36,7 @@ void main() { 'A test to verify a socket connection by passing useWebSocket to false and do a cram authenticate and scan', () async { var atLookup = AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); var command = 'scan\n'; var response = await atLookup.executeCommand(command, auth: true); @@ -48,7 +47,7 @@ void main() { 'A test to verify a websocket connection and do a cram authenticate and update', () async { var atLookup = AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); await atLookup.cramAuthenticate(at_demos.cramKeyMap[atSign]!); // update public and private keys manually var command = @@ -67,7 +66,7 @@ void main() { 'A test to verify a websocket connection and do a pkam authenticate and executeCommand', () async { var atLookup = AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = atChopsKeys; await atLookup.pkamAuthenticate(); var command = 'update:public:username$atSign bob123\n'; @@ -80,7 +79,7 @@ void main() { 'A test to verify a websocket connection and do a pkam authenticate and execute verb', () async { var atLookup = AtLookupImpl(atSign, 'vip.ve.atsign.zone', 64, - atOutboundConnectionFactory: AtLookupWebSocketFactory()); + atConnectionFactory: AtLookupWebSocketFactory()); atLookup.atChops = atChopsKeys; await atLookup.pkamAuthenticate(); var atKey = 'key1';