import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; /// iOS 스토어 제출 구성 검사. /// /// 여기 있는 것들은 사라져도 앱이 멀쩡히 빌드된다. 문제는 업로드·심사·로그인 /// 시점에야 드러나고, 그때는 원인이 코드처럼 보인다. 파일을 문자열로 읽어 /// 키가 있는지만 본다 — 값의 해석은 빌드가 한다. const _teamId = 'V28G543VK5'; // Fancake Inc. String _read(String path) { final file = File(path); expect(file.existsSync(), isTrue, reason: '$path 가 없습니다'); return file.readAsStringSync(); } void main() { group('iOS — 서명', () { test('Xcode 프로젝트와 내보내기 설정이 발주사 팀으로 서명한다', () { final pbx = _read('ios/Runner.xcodeproj/project.pbxproj'); final teams = RegExp(r'DEVELOPMENT_TEAM = (\w+);') .allMatches(pbx) .map((m) => m.group(1)) .toSet(); expect(teams, {_teamId}); expect(_read('ios/ExportOptions.plist'), contains(_teamId)); }); test('Sign in with Apple Capability 가 서명에 연결되어 있다', () { expect( _read('ios/Runner/Runner.entitlements'), contains('com.apple.developer.applesignin'), ); expect( 'CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;' .allMatches(_read('ios/Runner.xcodeproj/project.pbxproj')) .length, 3, // Debug · Release · Profile ); }); }); group('iOS — 제출 선언과 Firebase 설정', () { late final String plist = _read('ios/Runner/Info.plist'); test('암호화 사용 신고가 있어 업로드가 멈추지 않는다', () { expect(plist, contains('ITSAppUsesNonExemptEncryption')); }); test('Google 로그인 콜백 스킴이 설정 파일 값으로 채워진다', () { expect(plist, contains(r'$(GOOGLE_REVERSED_CLIENT_ID)')); for (final c in const ['Debug', 'Release']) { expect( _read('ios/Flutter/$c.xcconfig'), contains('#include? "../config/Google.xcconfig"'), reason: c, ); } }); test('GoogleService-Info.plist 가 앱 번들 리소스로 들어간다', () { // 번들에 없으면 Firebase 가 목 모드로 뜨고 Google 로그인 client ID 도 못 읽는다. expect( _read('ios/Runner.xcodeproj/project.pbxproj'), contains('/* GoogleService-Info.plist in Resources */,'), ); }); }); test('Android — MainActivity 가 패키지 기본 task 에 있어 LINE 로그인 후 앱으로 돌아온다', () { // LINE SDK 는 lineauth:// 복귀 시 결과 화면을 NEW_TASK|CLEAR_TOP 으로 띄워 패키지 // affinity 의 task 를 찾는다. MainActivity 의 taskAffinity 가 비어 있으면 새 task 가 // 생겨 Chrome 맞춤 탭이 앱 위에 남는다(사용자가 X 를 눌러야 로그인이 끝난다). final manifest = _read('android/app/src/main/AndroidManifest.xml'); final mainActivity = RegExp( r']*android:name="\.MainActivity"[^>]*>', ).firstMatch(manifest)?.group(0); expect(mainActivity, isNotNull); expect(mainActivity, isNot(contains('taskAffinity'))); }); test('설정·서명 자산을 git 이 추적하지 않는다', () { final result = Process.runSync('git', [ 'ls-files', '--', 'ios/Runner/GoogleService-Info.plist', 'ios/config/Google.xcconfig', 'android/app/google-services.json', '*.p12', '*.p8', '*.mobileprovision', ]); expect(result.exitCode, 0, reason: 'git 을 실행하지 못했습니다'); expect((result.stdout as String).trim(), isEmpty); }); }