nomadcode/apps/client/test/integrations/proto_socket_lifecycle_test.dart
toki 217c17dfef refactor: rename apps/mobile to apps/client and update project structure
- Rename mobile app directory to client throughout the project
- Restructure client app code into feature-based organization
  - Move code to src/features/workspaces/domain/presentation
  - Move services to src/integrations/ (mattermost, workspace, proto_socket)
  - Add app bootstrap and main entry point
- Add push notification integration (Mattermost push client/host/plugin)
- Add proto socket integration for real-time communication
- Add integration tests for push and socket components
- Archive old milestone: client-integration-standardization.md
- Add new workflow core milestone: roadmap-driven-agent-ops-automation.md
- Update agent-ops rules (project, core, mobile, contracts, workspace-ops)
- Update roadmap files (ROADMAP.md, current.md, phase PHASE.md files)
- Update bin scripts (build, dev, lint, test)
- Add test environments documentation
- Update contracts notes for Flutter Core API
2026-05-25 22:10:43 +09:00

89 lines
2.7 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:nomadcode_app/src/integrations/proto_socket/proto_socket_endpoint_config.dart';
import 'package:nomadcode_app/src/integrations/proto_socket/proto_socket_lifecycle.dart';
class _FakeConnector implements ProtoSocketConnector {
int connectCalls = 0;
int disconnectCalls = 0;
Object? throwOnConnect;
@override
Future<void> connect(ProtoSocketEndpointConfig config) async {
connectCalls += 1;
final err = throwOnConnect;
if (err != null) throw err;
}
@override
Future<void> disconnect() async {
disconnectCalls += 1;
}
}
void main() {
const config = ProtoSocketEndpointConfig(
host: 'core.example.com',
port: 443,
secure: true,
);
test('happy path emits connecting -> connected -> disconnected', () async {
final fake = _FakeConnector();
final lifecycle = ProtoSocketLifecycle(connector: fake);
final states = <ProtoSocketConnectionState>[];
final sub = lifecycle.stateStream.listen(states.add);
expect(lifecycle.state, ProtoSocketConnectionState.disconnected);
await lifecycle.connect(config);
expect(lifecycle.state, ProtoSocketConnectionState.connected);
expect(fake.connectCalls, equals(1));
await lifecycle.disconnect();
expect(lifecycle.state, ProtoSocketConnectionState.disconnected);
expect(fake.disconnectCalls, equals(1));
await Future<void>.delayed(Duration.zero);
await sub.cancel();
await lifecycle.dispose();
expect(states, [
ProtoSocketConnectionState.connecting,
ProtoSocketConnectionState.connected,
ProtoSocketConnectionState.disconnected,
]);
});
test('connect failure transitions to failed and records error', () async {
final fake = _FakeConnector()..throwOnConnect = StateError('boom');
final lifecycle = ProtoSocketLifecycle(connector: fake);
await expectLater(lifecycle.connect(config), throwsA(isA<StateError>()));
expect(lifecycle.state, ProtoSocketConnectionState.failed);
expect(lifecycle.lastError, isA<StateError>());
await lifecycle.dispose();
});
test('connect is a no-op when already connecting or connected', () async {
final fake = _FakeConnector();
final lifecycle = ProtoSocketLifecycle(connector: fake);
await lifecycle.connect(config);
await lifecycle.connect(config);
expect(fake.connectCalls, equals(1));
await lifecycle.dispose();
});
test('disconnect from disconnected state is a no-op', () async {
final fake = _FakeConnector();
final lifecycle = ProtoSocketLifecycle(connector: fake);
await lifecycle.disconnect();
expect(fake.disconnectCalls, equals(0));
expect(lifecycle.state, ProtoSocketConnectionState.disconnected);
await lifecycle.dispose();
});
}