138 lines
6.2 KiB
Dart
138 lines
6.2 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:integration_test/integration_test.dart';
|
|
import 'package:jj_mkvmaker/app/app_controller.dart';
|
|
import 'package:jj_mkvmaker/app/bookmarks_controller.dart';
|
|
import 'package:jj_mkvmaker/app/component_store.dart';
|
|
import 'package:jj_mkvmaker/app/net_guard.dart';
|
|
import 'package:jj_mkvmaker/app/tor_bundle.dart';
|
|
import 'package:jj_mkvmaker/app/tor_service.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
|
import 'package:jj_mkvmaker/ui/app_actions.dart';
|
|
import 'package:jj_mkvmaker/ui/browser_page.dart';
|
|
import 'package:jj_mkvmaker/ui/tor_page.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// 실제 망 (Windows): DNS 강제 (8.8.8.8) 로 앱의 HTTP · 일반 브라우저 (Edge) 가 이름을 푸는지, 닿지 않는 DNS 는 실패로 알리는지,
|
|
/// Tor 컴포넌트 (공식 번들, SHA-256 확인) 를 받아 Tor 화면이 check.torproject.org 의 "Congratulations" 까지 가는지.
|
|
/// 데이터는 JJ_MKVMAKER_DATA (없으면 임시 폴더) 에만 - 사용자 앱 데이터를 건드리지 않는다.
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
testWidgets('DNS 강제 · Tor 화면 실제 연결', (tester) async {
|
|
final data = Platform.environment['JJ_MKVMAKER_DATA'] ?? Directory.systemTemp.createTempSync('jj_tor_real_').path;
|
|
Directory(data).createSync(recursive: true);
|
|
ComponentStore.dataDirectory = data;
|
|
ComponentStore.shared = ComponentStore(data);
|
|
TorService.dataDir = data;
|
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
|
c.settings.webViewDataDir = p.join(data, 'webview');
|
|
final logs = <String>[];
|
|
final answered = <String>[];
|
|
final g = NetGuard.instance
|
|
..config = (() => c.settings.dnsConfig)
|
|
..log = logs.add;
|
|
g.resolver.onAnswer = (h, s) => answered.add('$h@$s');
|
|
TorService.instance.log = logs.add;
|
|
expect(c.settings.dnsMode, 'custom');
|
|
expect(c.settings.dnsPrimary, '8.8.8.8');
|
|
await tester.runAsync(g.ensure);
|
|
HttpOverrides.global = JjHttpOverrides();
|
|
Future<void> wait(int ms) async {
|
|
for (var i = 0; i < ms ~/ 200; i++) {
|
|
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 200)));
|
|
await tester.pump();
|
|
}
|
|
}
|
|
|
|
// 1. 앱의 HTTP (HttpClient) → 앱 안 프록시 → 8.8.8.8
|
|
final code = await tester.runAsync(() async {
|
|
final cl = HttpClient();
|
|
final r = await (await cl.getUrl(Uri.parse('https://www.google.com/generate_204'))).close();
|
|
await r.drain<void>();
|
|
cl.close();
|
|
return r.statusCode;
|
|
});
|
|
// ignore: avoid_print
|
|
print('RESULT HttpClient google 204: $code, 답한 DNS: $answered');
|
|
expect(code, 204);
|
|
expect(answered, contains('www.google.com@8.8.8.8'));
|
|
|
|
// 2. 일반 브라우저 (Edge WebView2) 도 같은 프록시로
|
|
final bm = BookmarksController(p.join(data, 'bm.json'));
|
|
await tester.pumpWidget(MaterialApp(
|
|
builder: (context, child) => AppScope(controller: c, bookmarks: bm, child: child!),
|
|
home: BrowserPage(c: c, bookmarks: bm, initialUrl: 'https://example.com/'),
|
|
));
|
|
String title = '';
|
|
for (var i = 0; i < 60 && !title.contains('Example'); i++) {
|
|
await wait(500);
|
|
title = '${await tester.runAsync(() async => BrowserPage.debugNav?.evaluate('document.title'))}';
|
|
}
|
|
// ignore: avoid_print
|
|
print('RESULT Edge example.com 제목: $title, 답한 DNS: ${answered.where((a) => a.startsWith('example.com'))}');
|
|
expect(title, contains('Example'));
|
|
expect(answered, contains('example.com@8.8.8.8'));
|
|
await tester.pumpWidget(const SizedBox());
|
|
await wait(1000);
|
|
|
|
// 3. 닿지 않는 DNS (TEST-NET) → 기기 DNS 로 바꾸지 않고 실패 + 알림
|
|
c.settings
|
|
..dnsPrimary = '192.0.2.1'
|
|
..dnsSecondary = '';
|
|
g.resolver.clearCache();
|
|
final err = await tester.runAsync(() async {
|
|
final cl = HttpClient();
|
|
try {
|
|
final r = await (await cl.getUrl(Uri.parse('https://www.wikipedia.org/'))).close();
|
|
await r.drain<void>();
|
|
return 'HTTP ${r.statusCode}';
|
|
} catch (e) {
|
|
return '$e';
|
|
} finally {
|
|
cl.close(force: true);
|
|
}
|
|
});
|
|
// ignore: avoid_print
|
|
print('RESULT 닿지 않는 DNS: $err / 알림: ${g.failure.value?.reason}');
|
|
expect(err, contains('502'));
|
|
expect(g.failure.value, isNotNull);
|
|
c.settings
|
|
..dnsPrimary = '8.8.8.8'
|
|
..dnsSecondary = '8.8.4.4';
|
|
g.resolver.clearCache();
|
|
|
|
// 4. Tor 컴포넌트 받기 (공식 번들 · SHA-256) → Tor 화면 → check.torproject.org
|
|
await tester.runAsync(() => TorBundle.installWindows());
|
|
expect(TorBundle.windowsExe, isNotNull);
|
|
expect(TorBundle.installedVersion(), TorBundle.version);
|
|
c.settings
|
|
..components = [...c.settings.components, 'tor']
|
|
..torNoticeSeen = true;
|
|
await tester.pumpWidget(MaterialApp(
|
|
builder: (context, child) => AppScope(controller: c, bookmarks: bm, child: child!),
|
|
home: TorPage(c: c),
|
|
));
|
|
var body = '';
|
|
for (var i = 0; i < 360 && !body.contains('Congratulations'); i++) {
|
|
await wait(500);
|
|
if (TorPage.debugNav != null) body = '${await tester.runAsync(() async => TorPage.debugNav!.evaluate('document.body ? document.body.innerText : ""'))}';
|
|
}
|
|
final rtc = await tester.runAsync(() async => TorPage.debugNav?.evaluate('typeof RTCPeerConnection'));
|
|
// ignore: avoid_print
|
|
print('RESULT Tor 상태: ${TorService.instance.status.value.phase} / RTCPeerConnection: $rtc\n'
|
|
'RESULT check.torproject.org: ${body.split('\n').where((l) => l.trim().isNotEmpty).take(3).join(' | ')}');
|
|
// ignore: avoid_print
|
|
print('RESULT 기록: ${logs.where((l) => l.contains('Tor')).join(' | ')}');
|
|
expect(body, contains('Congratulations'));
|
|
expect(rtc, 'undefined');
|
|
await tester.pumpWidget(const SizedBox());
|
|
await tester.runAsync(() => TorService.instance.stop());
|
|
await wait(1000);
|
|
}, timeout: const Timeout(Duration(minutes: 10)));
|
|
}
|