Skip to content

Commit

Permalink
Merge pull request #585 from atsign-foundation/unit_tests_4
Browse files Browse the repository at this point in the history
  • Loading branch information
gkc authored Nov 22, 2023
2 parents 5cd9a73 + 5b4fe9f commit e116787
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import 'package:noports_core/sshnp_foundation.dart';

mixin SshnpDartSshKeyHandler on SshnpCore implements SshnpKeyHandler {
mixin SshnpDartSshKeyHandler implements SshnpKeyHandler {
@override
DartSshKeyUtil get keyUtil => _sshKeyUtil;
final DartSshKeyUtil _sshKeyUtil = DartSshKeyUtil();

@override
AtSshKeyPair? get identityKeyPair => _identityKeyPair;
AtSshKeyPair? _identityKeyPair;
AtSshKeyPair? identityKeyPair;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:noports_core/src/common/io_types.dart';
import 'package:noports_core/sshnp_foundation.dart';

/// [SshnpLocalSshKeyHandler] uses variables from [SshnpCore] so it is a mixin
/// on [SshnpCore]
mixin SshnpLocalSshKeyHandler on SshnpCore implements SshnpKeyHandler {
@override
LocalSshKeyUtil get keyUtil => _sshKeyUtil;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import 'package:noports_core/utils.dart';

mixin SshnpKeyHandler {
@protected
@visibleForTesting
AtSshKeyUtil get keyUtil;

@protected
@visibleForTesting
AtSshKeyPair? get identityKeyPair;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:noports_core/sshnp_foundation.dart';
import 'package:test/test.dart';

void main() {
group('SshnpDeviceList', () {
late SshnpDeviceList deviceList;
setUp(() => deviceList = SshnpDeviceList());
test('public API', () {
expect(
deviceList.info,
allOf(isA<Map<String, dynamic>>(), isEmpty),
);

expect(
deviceList.activeDevices,
allOf(isA<Set<String>>(), isEmpty),
);
}); // test public API

test('setActive', () {
expect(deviceList.info, isEmpty);

deviceList.info['dev1'] = 'asdf';
deviceList.setActive('dev1');
expect(
deviceList.activeDevices,
allOf(hasLength(1), contains('dev1')),
);

deviceList.setActive('dev2');
expect(
deviceList.activeDevices,
allOf(hasLength(1), isNot(contains('dev2'))),
);
});

test('inactiveDevices', () {
deviceList.info['dev1'] = 'asdf';
deviceList.info['dev2'] = 'jkl;';
deviceList.setActive('dev1');

expect(
deviceList.inactiveDevices,
allOf(hasLength(1), contains('dev2')),
);
}); // test inactiveDevices
}); // group SshnpDeviceList
}
4 changes: 2 additions & 2 deletions packages/noports_core/test/sshnp/sshnp_core_mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import 'sshnp_core_constants.dart';

/// Mocked Classes
/// Stubbed [SshnpCore] (minimum viable implementation of [SshnpCore])
class StubbedSshnpCore extends SshnpCore with StubbedAsyncInitializationMixin {
StubbedSshnpCore({
class StubbedSshnp extends SshnpCore with StubbedAsyncInitializationMixin {
StubbedSshnp({
required super.atClient,
required super.params,
SshnpdChannel? sshnpdChannel,
Expand Down
10 changes: 5 additions & 5 deletions packages/noports_core/test/sshnp/sshnp_core_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void main() {
stubbedCompleteInitialization = FunctionStub();
});

/// When declaration setup for the constructor of [StubbedSshnpCore]
/// When declaration setup for the constructor of [StubbedSshnp]
whenConstructor({bool verbose = false}) {
when(() => mockParams.device).thenReturn('mydevice');
when(() => mockParams.localPort).thenReturn(0);
Expand All @@ -44,7 +44,7 @@ void main() {
when(() => mockAtClient.setPreferences(any())).thenReturn(null);
}

/// When declaration setup for the initialization of [StubbedSshnpCore]
/// When declaration setup for the initialization of [StubbedSshnp]
whenInitialization({AtSshKeyPair? identityKeyPair}) {
when(() => stubbedCallInitialization()).thenAnswer((_) async {});
when(() => stubbedInitialize()).thenAnswer((_) async {});
Expand All @@ -65,7 +65,7 @@ void main() {
whenConstructor(verbose: false);

final sshnpCore =
StubbedSshnpCore(atClient: mockAtClient, params: mockParams);
StubbedSshnp(atClient: mockAtClient, params: mockParams);

/// Expect that the namespace is set in the preferences
verify(() => mockAtClient.getPreferences()).called(1);
Expand All @@ -81,7 +81,7 @@ void main() {
whenConstructor(verbose: true);

final sshnpCore =
StubbedSshnpCore(atClient: mockAtClient, params: mockParams);
StubbedSshnp(atClient: mockAtClient, params: mockParams);

/// Expect that the namespace is set in the preferences
verify(() => mockAtClient.getPreferences()).called(1);
Expand All @@ -99,7 +99,7 @@ void main() {
test('AsyncInitialization', () async {
whenConstructor();

final sshnpCore = StubbedSshnpCore(
final sshnpCore = StubbedSshnp(
atClient: mockAtClient,
params: mockParams,
sshnpdChannel: mockSshnpdChannel,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:noports_core/sshnp_foundation.dart';
import 'package:test/test.dart';

import 'sshnp_ssh_key_handler_mocks.dart';

void main() {
group('SshnpDartSshKeyHandler', () {
late MockSshnpDartSshKeyHandler keyHandler;
late MockAtSshKeyPair mockKeyPair;

setUp(() {
keyHandler = MockSshnpDartSshKeyHandler();
mockKeyPair = MockAtSshKeyPair();
});

test('public API', () {
/// The Dart key handler requires that there is a setter for
/// identityKeyPair, in addition to a getter
keyHandler.identityKeyPair = mockKeyPair;
expect(keyHandler.identityKeyPair, mockKeyPair);

expect(MockSshnpDartSshKeyHandler(), isA<SshnpKeyHandler>());
}); // test initialization
}); // group SshnpDartKeyHandler
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'package:at_client/at_client.dart';
import 'package:noports_core/sshnp_foundation.dart';
import 'package:test/test.dart';
import 'package:mocktail/mocktail.dart';

import '../../sshnp_mocks.dart';
import 'sshnp_ssh_key_handler_mocks.dart';

void main() {
group('SshnpLocalSshKeyHandler', () {
late MockAtClient mockAtClient;
late MockSshnpParams mockParams;
late MockLocalSshKeyUtil keyUtil;
late MockAtSshKeyPair keyPair;

late MockSshnpdChannel mockSshnpdChannel;
late MockSshrvdChannel mockSshrvdChannel;

setUp(() {
mockAtClient = MockAtClient();
mockParams = MockSshnpParams();
keyUtil = MockLocalSshKeyUtil();
keyPair = MockAtSshKeyPair();

mockSshnpdChannel = MockSshnpdChannel();
mockSshrvdChannel = MockSshrvdChannel();
registerFallbackValue(AtClientPreference());
});

whenConstructor() {
when(() => mockParams.device).thenReturn('mydevice');
when(() => mockParams.localPort).thenReturn(0);
when(() => mockParams.verbose).thenReturn(false);
when(() => mockAtClient.getPreferences()).thenReturn(null);
when(() => mockAtClient.setPreferences(any())).thenReturn(null);
}

whenInitialization({AtSshKeyPair? identityKeyPair}) {
when(() => mockSshnpdChannel.callInitialization())
.thenAnswer((_) async {});
when(() => mockSshnpdChannel.resolveRemoteUsername())
.thenAnswer((_) async => 'myRemoteUsername');
when(() => mockSshnpdChannel.sharePublicKeyIfRequired(identityKeyPair))
.thenAnswer((_) async {});
when(() => mockSshrvdChannel.callInitialization())
.thenAnswer((_) async {});
}

test('public API', () {
whenConstructor();
final sshnp = StubbedSshnp(
atClient: mockAtClient,
params: mockParams,
);
expect(sshnp, isA<SshnpKeyHandler>());
}); // test public API

test('initialization', () async {
whenConstructor();

final sshnp = StubbedSshnp(
atClient: mockAtClient,
params: mockParams,
sshKeyUtil: keyUtil,
sshnpdChannel: mockSshnpdChannel,
sshrvdChannel: mockSshrvdChannel,
);

whenInitialization(identityKeyPair: keyPair);
final identityFile = '.ssh/asdf';
when(() => keyUtil.isValidPlatform).thenReturn(true);
when(() => mockParams.identityFile).thenReturn(identityFile);
when(() => keyUtil.getKeyPair(identifier: identityFile))
.thenAnswer((_) async => keyPair);

/// normally we would call [callInitialization()] but it's fine to call
/// initialize directly for testing purposes, since we avoid weird
/// lifecycle issues that could be caused by mocking
await sshnp.initialize();

/// We don't care about [SshnpCore] initialization here, we only care that
/// the [keyPair] is set correctly, since [SshnpCore] is tested elsewhere
expect(sshnp.identityKeyPair, keyPair);
}); // test initialization

test('initialization - no identityFile', () async {
whenConstructor();

final sshnp = StubbedSshnp(
atClient: mockAtClient,
params: mockParams,
sshKeyUtil: keyUtil,
sshnpdChannel: mockSshnpdChannel,
sshrvdChannel: mockSshrvdChannel,
);

whenInitialization(identityKeyPair: keyPair);
when(() => keyUtil.isValidPlatform).thenReturn(true);
when(() => mockParams.identityFile).thenReturn(null);
when(() => mockSshnpdChannel.sharePublicKeyIfRequired(null))
.thenAnswer((_) async {});
when(() => keyUtil.getKeyPair(identifier: '.ssh/asdf'))
.thenAnswer((_) async => keyPair);

/// normally we would call [callInitialization()] but it's fine to call
/// initialize directly for testing purposes, since we avoid weird
/// lifecycle issues that could be caused by mocking
await sshnp.initialize();

/// We don't care about [SshnpCore] initialization here, we only care that
/// the [keyPair] is set correctly, since [SshnpCore] is tested elsewhere
expect(sshnp.identityKeyPair, null);
}); // test initialization - no identityFile

test('initialization - invalid platform', () {
whenConstructor();

final sshnp = StubbedSshnp(
atClient: mockAtClient,
params: mockParams,
sshKeyUtil: keyUtil,
sshnpdChannel: mockSshnpdChannel,
sshrvdChannel: mockSshrvdChannel,
);

whenInitialization();
when(() => keyUtil.isValidPlatform).thenReturn(false);
expect(sshnp.initialize(), throwsA(isA<SshnpError>()));
});
}); // group SshnpLocalSshKeyHandler
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import 'package:mocktail/mocktail.dart';
import 'package:noports_core/sshnp_foundation.dart';

class MockSshnpKeyHandler extends Mock with SshnpKeyHandler {}

class MockAtSshKeyUtil extends Mock implements AtSshKeyUtil {}

class MockAtSshKeyPair extends Mock implements AtSshKeyPair {}

class MockSshnpDartSshKeyHandler extends Mock with SshnpDartSshKeyHandler {}

class StubbedSshnp extends SshnpCore with SshnpLocalSshKeyHandler {
@override
LocalSshKeyUtil get keyUtil => _sshKeyUtil ?? (throw UnimplementedError());
final LocalSshKeyUtil? _sshKeyUtil;

StubbedSshnp({
required super.atClient,
required super.params,
LocalSshKeyUtil? sshKeyUtil,
SshnpdChannel? sshnpdChannel,
SshrvdChannel? sshrvdChannel,
}) : _sshKeyUtil = sshKeyUtil,
_sshnpdChannel = sshnpdChannel,
_sshrvdChannel = sshrvdChannel;

@override
Future<SshnpResult> run() => throw UnimplementedError();

@override
SshnpdChannel get sshnpdChannel =>
_sshnpdChannel ?? (throw UnimplementedError());
final SshnpdChannel? _sshnpdChannel;

@override
SshrvdChannel get sshrvdChannel =>
_sshrvdChannel ?? (throw UnimplementedError());
final SshrvdChannel? _sshrvdChannel;
}

class MockLocalSshKeyUtil extends Mock implements LocalSshKeyUtil {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:mocktail/mocktail.dart';
import 'package:noports_core/sshnp_foundation.dart';
import 'package:test/test.dart';

import 'sshnp_ssh_key_handler_mocks.dart';

void main() {
group('SshnpKeyHandler', () {
late MockSshnpKeyHandler mockKeyHandler;
late MockAtSshKeyUtil mockKeyUtil;
late MockAtSshKeyPair mockKeyPair;

setUp(() {
mockKeyHandler = MockSshnpKeyHandler();
mockKeyUtil = MockAtSshKeyUtil();
mockKeyPair = MockAtSshKeyPair();
});

test('public API', () {
when(() => mockKeyHandler.keyUtil).thenReturn(mockKeyUtil);
when(() => mockKeyHandler.identityKeyPair).thenReturn(mockKeyPair);

expect(mockKeyHandler.keyUtil, isA<AtSshKeyUtil>());
expect(mockKeyHandler.identityKeyPair, isA<AtSshKeyPair?>());
}); // test public API
}); // group SshnpKeyHandler
}

0 comments on commit e116787

Please sign in to comment.