1 // Copyright 2026 Onde Inference (Splitfire AB). All rights reserved.
2 // Use of this source code is governed by the MIT license.
3 //
4 import 'package:flutter_test/flutter_test.dart';
5 import 'package:onde_inference/onde_inference.dart';
6
7 void main() {
8 group('OndeException', () {
9 test('toString includes error info', () {
10 const e = OndeException(OndeError_NoModelLoaded());
11 expect(e.toString(), contains('OndeException'));
12 });
13
14 test('equality', () {
15 const a = OndeException(OndeError_NoModelLoaded());
16 const b = OndeException(OndeError_NoModelLoaded());
17 const c = OndeException(OndeError_Cancelled());
18 expect(a, equals(b));
19 expect(a, isNot(equals(c)));
20 });
21 });
22
23 group('ChatRole', () {
24 test('name values', () {
25 expect(ChatRole.system.name, 'system');
26 expect(ChatRole.user.name, 'user');
27 expect(ChatRole.assistant.name, 'assistant');
28 });
29 });
30
31 group('ChatMessage', () {
32 test('factory constructors set correct roles', () {
33 expect(ChatMessageX.system('Hi').role, ChatRole.system);
34 expect(ChatMessageX.user('Hi').role, ChatRole.user);
35 expect(ChatMessageX.assistant('Hi').role, ChatRole.assistant);
36 });
37
38 test('equality', () {
39 final a = ChatMessageX.user('Hello');
40 final b = ChatMessageX.user('Hello');
41 final c = ChatMessageX.user('World');
42 expect(a, equals(b));
43 expect(a, isNot(equals(c)));
44 });
45 });
46
47 group('SamplingConfig', () {
48 test('defaultConfig preset', () {
49 final s = SamplingConfigX.defaultConfig();
50 expect(s.temperature, 0.7);
51 expect(s.topP, 0.95);
52 expect(s.maxTokens, BigInt.from(512));
53 });
54
55 test('deterministic preset', () {
56 final s = SamplingConfigX.deterministic();
57 expect(s.temperature, 0.0);
58 expect(s.maxTokens, BigInt.from(512));
59 expect(s.topP, isNull);
60 });
61
62 test('mobile preset', () {
63 final s = SamplingConfigX.mobile();
64 expect(s.temperature, 0.7);
65 expect(s.topP, 0.95);
66 expect(s.maxTokens, BigInt.from(128));
67 });
68
69 test('copyWith overrides single field', () {
70 final base = SamplingConfigX.defaultConfig();
71 final copy = base.copyWith(temperature: 0.3);
72 expect(copy.temperature, 0.3);
73 expect(copy.topP, base.topP);
74 expect(copy.maxTokens, base.maxTokens);
75 });
76
77 test('equality', () {
78 expect(
79 SamplingConfigX.deterministic(),
80 equals(SamplingConfigX.deterministic()),
81 );
82 expect(
83 SamplingConfigX.deterministic(),
84 isNot(equals(SamplingConfigX.mobile())),
85 );
86 });
87 });
88
89 group('GgufModelConfig', () {
90 test('factory functions return non-empty configs', () {
91 final config1_5b = OndeInference.qwen2515bConfig();
92 expect(config1_5b.modelId, contains('1.5B'));
93 expect(config1_5b.files, isNotEmpty);
94 expect(config1_5b.displayName, isNotEmpty);
95 expect(config1_5b.approxMemory, isNotEmpty);
96
97 final config3b = OndeInference.qwen253bConfig();
98 expect(config3b.modelId, contains('3B'));
99 expect(config3b.files, isNotEmpty);
100
101 final configCoder1_5b = OndeInference.qwen25Coder15bConfig();
102 expect(configCoder1_5b.modelId, contains('Coder'));
103
104 final configCoder3b = OndeInference.qwen25Coder3bConfig();
105 expect(configCoder3b.modelId, contains('Coder'));
106
107 final defaultConfig = OndeInference.defaultModelConfig();
108 expect(defaultConfig.modelId, isNotEmpty);
109 });
110
111 test('equality', () {
112 expect(
113 OndeInference.qwen2515bConfig(),
114 equals(OndeInference.qwen2515bConfig()),
115 );
116 expect(
117 OndeInference.qwen2515bConfig(),
118 isNot(equals(OndeInference.qwen253bConfig())),
119 );
120 });
121 });
122
123 group('EngineStatus', () {
124 test('isReady only true for ready', () {
125 expect(EngineStatus.ready.isReady, isTrue);
126 expect(EngineStatus.unloaded.isReady, isFalse);
127 expect(EngineStatus.loading.isReady, isFalse);
128 expect(EngineStatus.generating.isReady, isFalse);
129 expect(EngineStatus.error.isReady, isFalse);
130 });
131
132 test('name values', () {
133 expect(EngineStatus.unloaded.name, 'unloaded');
134 expect(EngineStatus.loading.name, 'loading');
135 expect(EngineStatus.ready.name, 'ready');
136 expect(EngineStatus.generating.name, 'generating');
137 expect(EngineStatus.error.name, 'error');
138 });
139 });
140
141 group('EngineInfo', () {
142 test('equality', () {
143 final a = EngineInfo(
144 status: EngineStatus.unloaded,
145 historyLength: BigInt.zero,
146 );
147 final b = EngineInfo(
148 status: EngineStatus.unloaded,
149 historyLength: BigInt.zero,
150 );
151 final c = EngineInfo(
152 status: EngineStatus.ready,
153 modelName: 'Qwen 2.5 3B',
154 approxMemory: '~1.93 GB',
155 historyLength: BigInt.from(3),
156 );
157 expect(a, equals(b));
158 expect(a, isNot(equals(c)));
159 });
160 });
161
162 group('StreamChunk', () {
163 test('equality', () {
164 const a = StreamChunk(delta: 'hello', done: false);
165 const b = StreamChunk(delta: 'hello', done: false);
166 const c = StreamChunk(delta: 'bye', done: true, finishReason: 'stop');
167 expect(a, equals(b));
168 expect(a, isNot(equals(c)));
169 });
170 });
171
172 group('InferenceResult', () {
173 test('equality', () {
174 const a = InferenceResult(
175 text: 'Hi there!',
176 durationSecs: 1.5,
177 durationDisplay: '1.5s',
178 finishReason: 'stop',
179 toolCalls: [],
180 );
181 const b = InferenceResult(
182 text: 'Hi there!',
183 durationSecs: 1.5,
184 durationDisplay: '1.5s',
185 finishReason: 'stop',
186 toolCalls: [],
187 );
188 expect(a, equals(b));
189 });
190 });
191
192 group('RustLib', () {
193 test('instance is accessible before init', () {
194 // instance is always non-null; initialization requires the native
195 // library to be compiled first.
196 expect(RustLib.instance, isNotNull);
197 });
198 });
199
200 group('OndeInference helpers', () {
201 test('init() delegates to RustLib.init()', () async {
202 await expectLater(OndeInference.init(), completes);
203 });
204
205 test('config factories return valid configs', () {
206 expect(OndeInference.defaultModelConfig().modelId, isNotEmpty);
207 expect(OndeInference.qwen2515bConfig().modelId, contains('1.5B'));
208 expect(OndeInference.qwen253bConfig().modelId, contains('3B'));
209 expect(OndeInference.qwen25Coder15bConfig().modelId, contains('Coder'));
210 expect(OndeInference.qwen25Coder3bConfig().modelId, contains('Coder'));
211 });
212
213 test('sampling factories return correct presets', () {
214 expect(OndeInference.defaultSamplingConfig().temperature, 0.7);
215 expect(OndeInference.deterministicSamplingConfig().temperature, 0.0);
216 expect(OndeInference.mobileSamplingConfig().maxTokens, BigInt.from(128));
217 });
218 });
219
220 group('OndeChatEngine', () {
221 test('constructor throws when library not initialized', () {
222 expect(() => OndeChatEngine(), throwsA(anything));
223 });
224 });
225 }