chain-studio/test/ollama_service_suggestion_test.dart
flemming-it 14f824b8ef
Some checks failed
Security / Security check (push) Failing after 2s
feat(doctor,shell): ollama host-service suggestion + hub-update hint (0.80.0)
Doctor: when the system AI uses an Ollama endpoint that no declared
host service covers (host:port match, /v1 suffix stripped), the
services panel says so in one sentence with a one-click 'declare as
host service' via the new DeclareService RPC — and states honestly
that it takes effect after a daemon restart (the restart button sits
on the same page). Pure suggestOllamaServiceEndpoint pins every
branch.

Shell: one slim, dismissible banner after connecting when the
release manifest offers a newer hub version; dismissal is persisted
per version so each release hints exactly once (pure
shouldShowUpdateHint + a widget test through the fake hub).
Deliberately manifest-based — Studio and hub versions are
independent counters, so a direct comparison would be wrong;
unreleased dev skew stays with the per-page classified states.
The probe stays inert under the test probe override: its timeout
timer leaked into hub_banner_test (the hermeticity class again).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-07-23 00:03:42 +02:00

149 lines
4.1 KiB
Dart

// The doctor's "declare the system AI's Ollama as a host service"
// suggestion and the shell's one-time update hint — both are pure
// functions so every branch is pinned here.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:chain_studio/data/hub.dart';
import 'package:chain_studio/main.dart'
show StudioApp, shouldShowUpdateHint;
import 'package:chain_studio/pages/doctor.dart'
show suggestOllamaServiceEndpoint;
import 'support/fake_hub.dart';
SystemAiStatus _ai({
bool enabled = true,
String provider = 'ollama',
String endpoint = 'http://127.0.0.1:11434/v1',
}) => SystemAiStatus(
enabled: enabled,
provider: provider,
endpoint: endpoint,
model: 'qwen2.5:14b',
privacyMode: 'off',
apiKeyEnv: '',
);
void main() {
group('suggestOllamaServiceEndpoint', () {
test('suggests the base URL with the /v1 suffix stripped', () {
expect(
suggestOllamaServiceEndpoint(ai: _ai(), services: const []),
'http://127.0.0.1:11434',
);
});
test('nothing to suggest when AI is off or another provider', () {
expect(
suggestOllamaServiceEndpoint(ai: _ai(enabled: false), services: const []),
isNull,
);
expect(
suggestOllamaServiceEndpoint(
ai: _ai(provider: 'openai'),
services: const [],
),
isNull,
);
expect(
suggestOllamaServiceEndpoint(ai: _ai(endpoint: ''), services: const []),
isNull,
);
});
test('a declared service on the same host:port silences it', () {
expect(
suggestOllamaServiceEndpoint(
ai: _ai(),
services: const [
ServiceEntry(
name: 'my-llm',
endpoint: 'http://127.0.0.1:11434',
tags: [],
),
],
),
isNull,
);
});
test('a service on another port does not silence it', () {
expect(
suggestOllamaServiceEndpoint(
ai: _ai(),
services: const [
ServiceEntry(
name: 'judge-ner',
endpoint: 'http://127.0.0.1:8756',
tags: [],
),
],
),
'http://127.0.0.1:11434',
);
});
});
testWidgets('the shell hints once and dismisses persistently', (
tester,
) async {
SharedPreferences.setMockInitialValues({});
final fake = installFakeHub();
fake.updateHint = const UpdateStatus(
channel: 'stable',
localVersion: '0.22.0',
latestVersion: '0.23.0',
updateAvailable: true,
manifestReachable: true,
);
await tester.pumpWidget(
const StudioApp(
initialThemeMode: ThemeModeValue.dark,
initialLocale: Locale('de'),
),
);
for (var i = 0; i < 8; i++) {
await tester.pump(const Duration(milliseconds: 100));
}
expect(find.textContaining('0.23.0'), findsOneWidget);
await tester.tap(find.text('Später'));
await tester.pump(const Duration(milliseconds: 100));
expect(find.textContaining('0.23.0'), findsNothing);
// Dismissal is persisted per version.
final prefs = await SharedPreferences.getInstance();
expect(prefs.getString('update.hint.dismissed'), '0.23.0');
// Drain shell timers (a11y-suite pattern).
await tester.pumpWidget(const SizedBox.shrink());
await tester.pump(const Duration(minutes: 1));
});
group('shouldShowUpdateHint', () {
test('hints once per release version', () {
expect(
shouldShowUpdateHint(latestVersion: '0.23.0', dismissedVersion: null),
isTrue,
);
expect(
shouldShowUpdateHint(
latestVersion: '0.23.0',
dismissedVersion: '0.22.0',
),
isTrue,
);
expect(
shouldShowUpdateHint(
latestVersion: '0.23.0',
dismissedVersion: '0.23.0',
),
isFalse,
);
expect(
shouldShowUpdateHint(latestVersion: '', dismissedVersion: null),
isFalse,
);
});
});
}