test(mobile): exercise native signing in linked iOS Runner

Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
Tom Brow
2026-10-18 18:40:54 -07:00
parent 46a98f759c
commit 8e60b7608c
3 changed files with 49 additions and 1 deletions

View File

@@ -248,11 +248,18 @@ jobs:
tail -n 5 "$build_log"
"${xcode[@]}" -parallel-testing-enabled NO -only-testing:RunnerTests \
test-without-building
/bin/bash scripts/test-mobile-linked-signing.sh "$device"
- name: Build complete unsigned iOS release
if: matrix.lane == 'release'
run: |
/bin/bash scripts/test-mobile-gateway-recipes.sh
cd mobile && flutter build ios --release --no-codesign --no-pub
# Flutter does not support Release mode on the simulator. Check the
# actual device Release executable's exported symbols separately.
nm -gU build/ios/iphoneos/Buzz.app/Buzz > /tmp/buzz-release-symbols
for symbol in buzz_signer_create buzz_signer_sign buzz_signer_destroy; do
grep -Eq "[[:space:]]_${symbol}$" /tmp/buzz-release-symbols
done
results:
name: Results

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:nostr/nostr.dart' as nostr;
import 'package:buzz/shared/relay/media_auth.dart';
@@ -7,6 +8,7 @@ import 'package:buzz/shared/crypto/event_signer.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!Platform.isIOS) throw StateError('This probe requires an iOS Runner');
final results = <String>[];
final key = '0' * 63 + '3';
final nsec = nostr.Nip19.encode(prefix: nostr.Nip19Prefix.nsec, data: key);
@@ -86,12 +88,16 @@ Future<void> main() async {
'32 complete NIP98 headers: ${queryWatch.elapsedMicroseconds} us',
);
for (final header in headers) {
nostr.Event.fromJson(utf8.decode(base64Decode(header.split(' ').last)));
final event = nostr.Event.fromJson(
utf8.decode(base64Decode(header.split(' ').last)),
);
if (!event.isValid()) throw StateError('Invalid query proof');
}
results.add('query signatures: PASS');
for (final result in results) {
debugPrint('SIGNING_PROBE $result');
}
debugPrint('SIGNING_PROBE COMPLETE');
runApp(
MaterialApp(
home: Scaffold(body: SafeArea(child: Text(results.join('\n')))),

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Run the Dart production signer through symbols exported by the actual Runner.
set -euo pipefail
cd "$(dirname "$0")/.."
. ./bin/activate-hermit
device=${1:?Pass a booted simulator UDID}
fixture=$(mktemp -d)
bundle=xyz.block.buzz.signing-probe
trap 'xcrun simctl terminate "$device" "$bundle" >/dev/null 2>&1 || true; rm -rf "$fixture"' EXIT
xcodebuild -workspace mobile/ios/Runner.xcworkspace -scheme Runner \
-configuration Debug -sdk iphonesimulator -destination "id=$device" \
-derivedDataPath "${BUZZ_SIGNING_DERIVED_DATA:-$fixture/build}" -jobs 2 \
BUNDLE_IDENTIFIER="$bundle" CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES \
FLUTTER_TARGET=tool/native_signing_probe.dart build > "$fixture/build.log" 2>&1 || {
cat "$fixture/build.log"
exit 1
}
xcrun simctl install "$device" "${BUZZ_SIGNING_DERIVED_DATA:-$fixture/build}/Build/Products/Debug-iphonesimulator/Buzz.app"
launch=$(xcrun simctl launch --terminate-running-process "$device" "$bundle")
probe_pid=${launch##*: }
[[ "$probe_pid" =~ ^[0-9]+$ ]] || { echo "Unexpected launch result: $launch" >&2; exit 1; }
# Flutter's iOS logger writes to the unified log, not redirected stdout.
for ((attempt = 0; attempt < 90; attempt++)); do
xcrun simctl spawn "$device" log show --last 5m --style compact \
--predicate "processIdentifier == $probe_pid AND eventMessage CONTAINS \"SIGNING_PROBE\"" \
> "$fixture/probe.log" 2>/dev/null
if grep -q 'SIGNING_PROBE COMPLETE' "$fixture/probe.log"; then
cat "$fixture/probe.log"
exit 0
fi
sleep 1
done
cat "$fixture/probe.log"
echo 'Linked iOS signing probe did not complete' >&2
exit 1