feat(store,doctor): surface the hub's trust + exposure data (0.77.0)
Some checks failed
Security / Security check (push) Failing after 1s

The trust-gate dialog replaces its generic 'no per-entry status yet
(alpha)' note with the hub's classified verification statement —
pinned store key / trusted publishers (green), installs without
signature checking (amber), install would be refused (red), bridge
entry (neutral) — via one shared describeInstallVerification mapping.
Against a pre-0.23 hub the field is empty and the old honest wording
stays (pinned by test).

Information architecture: policy-off is a GLOBAL fact, so it appears
as ONE ChainInlineHelp notice above the store grid instead of a
warning pill on every card (card noise); only 'blocked' — a genuine
per-source anomaly — earns a card pill. Doctor's host services show
the hub-classified network reach per endpoint (local only / private
network / publicly reachable with a protect-it hint / reach unknown).

Verified end-to-end against the live dev hub (guide harness): the
wire field arrives, the store page shows exactly one policy notice
and quiet cards; trust-gate variants captured via the dialog
harness. Suite 123 green.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-21 13:14:13 +02:00
parent 87afa4dc05
commit 2f076ccf29
15 changed files with 732 additions and 13 deletions

View file

@ -16,8 +16,10 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:chain_studio/data/hub.dart' show StoreItem;
import 'package:chain_studio/l10n/app_localizations.dart';
import 'package:chain_studio/pages/federation.dart';
import 'package:chain_studio/widgets/chain_install_confirm.dart';
import '../test/support/fake_hub.dart';
@ -41,6 +43,31 @@ Future<void> _shot(WidgetTester tester, String name) async {
print('dialog-shot: ${file.path}');
}
StoreItem _storeItem(String verification) => StoreItem(
name: 'text.extract',
taglineEn: 'Extract clean text',
taglineDe: 'Reinen Text extrahieren',
descriptionEn: '',
descriptionDe: '',
category: 'text',
tags: const [],
requiresCapabilities: const [],
requiresServices: const [],
license: 'Apache-2.0',
repository: '',
bestVersion: '0.1.0',
status: 'alpha',
installed: false,
featured: false,
iconUrl: '',
screenshotUrls: const [],
docsUrl: '',
kind: 'native',
provider: '',
source: 'bundled',
installVerification: verification,
);
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@ -76,4 +103,38 @@ void main() {
findsOneWidget);
});
}
// Trust gate with the hub-reported verification statement the
// pinned-key (good) and policy-off (warning) variants.
for (final (name, verification) in [
('trust-gate-pinned', 'pinned-key'),
('trust-gate-unverified', 'unverified'),
]) {
testWidgets('$name — dark', (tester) async {
SharedPreferences.setMockInitialValues({});
installFakeHub();
await tester.pumpWidget(
MaterialApp(
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.dark,
theme: ThemeData.light(useMaterial3: true),
darkTheme: ThemeData.dark(useMaterial3: true),
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
locale: const Locale('de'),
builder: (context, child) =>
RepaintBoundary(key: _shotKey, child: child),
home: Scaffold(
body: Center(
child: ChainInstallConfirmDialog(
item: _storeItem(verification),
),
),
),
),
);
await tester.pump(const Duration(milliseconds: 200));
await _shot(tester, '$name-dark');
});
}
}