forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenv_test.dart
More file actions
280 lines (254 loc) · 10.3 KB
/
Copy pathenv_test.dart
File metadata and controls
280 lines (254 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import 'package:flutter_test/flutter_test.dart';
import 'package:omi/env/env.dart';
import 'package:omi/env/environment_profile.dart';
import 'package:omi/flavors.dart';
import 'package:omi/startup_routing.dart';
import 'dart:io';
/// Minimal EnvFields stub for testing Env logic in isolation.
/// Since Env._instance is late final (can only be set once per process),
/// we test with a single init and exercise the override/flag mechanisms.
class _TestEnvFields implements EnvFields {
@override
String? get posthogApiKey => null;
@override
String? get apiBaseUrl => null;
@override
String? get intercomAppId => null;
@override
String? get intercomIOSApiKey => null;
@override
String? get intercomAndroidApiKey => null;
@override
String? get googleClientId => null;
@override
String? get googleClientSecret => null;
@override
bool? get useWebAuth => false;
@override
bool? get useAuthCustomToken => false;
}
void main() {
// Init once for the entire test suite (late final constraint)
setUpAll(() {
Env.init(_TestEnvFields());
});
group('Env.isTestFlight', () {
test('can be set to false', () {
Env.isTestFlight = false;
expect(Env.isTestFlight, isFalse);
});
test('can be set to true', () {
Env.isTestFlight = true;
expect(Env.isTestFlight, isTrue);
// Clean up
Env.isTestFlight = false;
});
});
group('mobile environment profiles', () {
test('local development is emulator-first and does not allow production data', () {
expect(AppEnvironmentProfile.localDev.defaultApiBaseUrl, 'http://127.0.0.1:8000/');
expect(AppEnvironmentProfile.localDev.firebaseProjectId, 'demo-omi-local');
expect(AppEnvironmentProfile.localDev.usesFirebaseAuthEmulator, isTrue);
expect(AppEnvironmentProfile.localDev.allowsProductionData, isFalse);
});
test('mobile beta explicitly pairs production Firebase with the dev serving plane', () {
expect(AppEnvironmentProfile.mobileBeta.defaultApiBaseUrl, 'https://api.omiapi.com/');
expect(AppEnvironmentProfile.mobileBeta.firebaseProjectId, 'based-hardware');
expect(AppEnvironmentProfile.mobileBeta.usesFirebaseAuthEmulator, isFalse);
expect(AppEnvironmentProfile.mobileBeta.allowsProductionData, isTrue);
expect(AppEnvironmentProfile.mobileBeta.authCallbackScheme, 'omi-beta');
});
test('mobile beta keeps OAuth on the production identity plane', () {
expect(
Env.authApiBaseUrlForProfile(AppEnvironmentProfile.mobileBeta, servingApiBaseUrl: 'https://api.omiapi.com/'),
Env.productionApiBaseUrl,
);
});
test('local prod pairs production Firebase with a developer-chosen backend', () {
expect(AppEnvironmentProfile.localProd.firebaseProjectId, 'based-hardware');
expect(AppEnvironmentProfile.localProd.usesFirebaseAuthEmulator, isFalse);
expect(AppEnvironmentProfile.localProd.allowsProductionData, isTrue);
expect(AppEnvironmentProfile.localProd.authCallbackScheme, 'omi');
});
test('local profile rejects a production Firebase project', () {
expect(
() =>
Env.validateFirebaseProject(projectId: 'based-hardware', configuredProfile: AppEnvironmentProfile.localDev),
throwsStateError,
);
});
test('flavor defaults map to production and local profiles', () {
expect(AppEnvironmentProfile.forFlavor(productionFlavor: true), AppEnvironmentProfile.production);
expect(AppEnvironmentProfile.forFlavor(productionFlavor: false), AppEnvironmentProfile.localDev);
});
test('production iOS config keeps the production Google redirect client id', () {
final prodConfig = File('ios/Flutter/prodRelease.xcconfig').readAsStringSync();
expect(
prodConfig,
contains('GOOGLE_REVERSE_CLIENT_ID=com.googleusercontent.apps.208440318997-ukinsq3sijhcetkhr26ssqp1terbq7as'),
);
expect(prodConfig, isNot(contains('GOOGLE_REVERSE_CLIENT_ID=com.googleusercontent.apps.1031333818730-')));
});
});
group('Env.apiBaseUrl', () {
test('uses the local emulator API when development env has no URL', () {
expect(Env.apiBaseUrl, 'http://127.0.0.1:8000/');
});
test('returns override when set', () {
Env.overrideApiBaseUrl('https://override.example.com/');
expect(Env.apiBaseUrl, 'https://override.example.com/');
Env.clearApiBaseUrlOverrideForTesting();
});
test('TestFlight production startup accepts the production API', () {
validateApplicationStartupRouting(environment: Environment.prod, configuredApiBaseUrl: 'https://api.omi.me/');
});
test('Android production startup accepts the production API', () {
validateApplicationStartupRouting(environment: Environment.prod, configuredApiBaseUrl: 'https://api.omi.me/');
});
test('mobile beta accepts the dev serving plane with production identity', () {
Env.validateStartupRouting(
productionFamily: true,
configuredProfile: AppEnvironmentProfile.mobileBeta,
configuredApiBaseUrl: 'https://api.omiapi.com/',
);
});
test('production startup rejects legacy Beta, dev, staging, and arbitrary endpoints', () {
for (final endpoint in [
'https://api-beta.omi.me/',
'https://api.omi.dev/',
'https://staging.example.test/',
'https://arbitrary.example.test/',
]) {
expect(
() => validateApplicationStartupRouting(environment: Environment.prod, configuredApiBaseUrl: endpoint),
throwsStateError,
reason: endpoint,
);
}
});
test('local dev accepts loopback and every private-network range, including CGNAT', () {
for (final endpoint in [
'http://127.0.0.1:8000/',
'http://localhost:8000/',
'http://10.0.0.5:8000/',
'http://172.16.0.5:8000/',
'http://172.31.255.254:8000/',
'http://192.168.1.20:8000/',
// 100.64.0.0/10 (RFC 6598, carrier-grade NAT) is the range Tailscale
// assigns. A physical device cannot reach the local harness any other
// way — the harness binds loopback only by design — so rejecting this
// range stranded the app on a blank splash with no diagnostic.
'http://100.64.0.1:8000/',
'http://100.105.2.5:8000/',
'http://100.127.255.254:8000/',
]) {
Env.validateStartupRouting(
productionFamily: false,
configuredProfile: AppEnvironmentProfile.localDev,
configuredApiBaseUrl: endpoint,
);
}
});
test('local dev still rejects public endpoints and the edges just outside CGNAT', () {
for (final endpoint in [
'https://api.omi.me/',
'https://api.omiapi.com/',
// 100.63.x and 100.128.x sit immediately outside 100.64.0.0/10 and must
// stay rejected — widening this must not degrade into "any 100.x host".
'http://100.63.255.255:8000/',
'http://100.128.0.1:8000/',
'http://8.8.8.8:8000/',
]) {
expect(
() => Env.validateStartupRouting(
productionFamily: false,
configuredProfile: AppEnvironmentProfile.localDev,
configuredApiBaseUrl: endpoint,
),
throwsStateError,
reason: endpoint,
);
}
});
test('local prod accepts loopback, private-network, and tunnel endpoints in debug builds', () {
for (final endpoint in [
'http://127.0.0.1:8000/',
'http://192.168.1.20:8000/',
'https://example.ngrok-free.app/',
]) {
Env.validateStartupRouting(
productionFamily: true,
configuredProfile: AppEnvironmentProfile.localProd,
configuredApiBaseUrl: endpoint,
releaseBuild: false,
);
}
});
test('local prod is rejected in release builds', () {
expect(
() => Env.validateStartupRouting(
productionFamily: true,
configuredProfile: AppEnvironmentProfile.localProd,
configuredApiBaseUrl: 'http://127.0.0.1:8000/',
releaseBuild: true,
),
throwsStateError,
);
});
test('local prod rejects a malformed endpoint', () {
expect(
() => Env.validateStartupRouting(
productionFamily: true,
configuredProfile: AppEnvironmentProfile.localProd,
configuredApiBaseUrl: 'not a url',
releaseBuild: false,
),
throwsStateError,
);
});
test('local development startup accepts the emulator API', () {
expect(
() => validateApplicationStartupRouting(
environment: Environment.dev,
configuredApiBaseUrl: 'http://127.0.0.1:8000/',
),
returnsNormally,
);
});
test('local development rejects the remote dev serving plane', () {
expect(
() => validateApplicationStartupRouting(
environment: Environment.dev,
configuredApiBaseUrl: 'https://api.omiapi.com/',
),
throwsStateError,
);
});
});
test('main invokes the production startup routing seam before services initialize', () {
// Static wiring tripwire: the behavioral cases above call the exact seam.
final mainSource = File('lib/main.dart').readAsStringSync();
expect(mainSource, contains('validateApplicationStartupRouting();'));
expect(
mainSource.indexOf('validateApplicationStartupRouting();'),
lessThan(mainSource.indexOf('ServiceManager.init()')),
);
// Same guarantee as before — the project id of the Firebase app main.dart ends
// up with is fed to Env.validateFirebaseProject — but the statement it used to
// name verbatim now lives in ensureFirebaseApp() (lib/startup_firebase.dart),
// which applies it to the freshly initialized app, the already-running native
// app, and the app adopted after [core/duplicate-app] alike. So this pins the
// wiring instead of one branch's text; that ensureFirebaseApp actually calls it
// on every one of those paths is asserted behaviorally in
// test/unit/startup_firebase_duplicate_app_test.dart.
expect(
mainSource,
matches(
RegExp(
r'projectIdOf:\s*\(app\)\s*=>\s*app\.options\.projectId,\s*'
r'validateProject:\s*\(projectId\)\s*=>\s*Env\.validateFirebaseProject\(projectId:\s*projectId\),',
),
),
);
});
}