mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-07-04 13:23:15 +02:00
Previously "Connect GitHub" (OAuth device flow) still required entering an OAuth App client id and owner/repo — friction that returned on every reinstall once shared_prefs were wiped. - Bake the app's own device-flow OAuth App client id in as SyncSettings.defaultClientId and default to it in load() (alongside the existing kuhyx/todo-sync repo default). A device-flow client id is a public identifier, not a secret, so it is safe to commit. - Settings now leads with a single "Connect GitHub" button; the manual client-id / token fields and Test connection move under an "Advanced" expander. Result: fresh install (or post-reinstall) is one tap → authorize the code in the browser → synced. No tokens, no setup. Note: an OAuth App authorizes with the classic `repo` scope (all repos), broader than the prior fine-grained PAT — the trade-off for one-tap device-flow convenience. 151 tests, 100% line coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
78 lines
2.1 KiB
Dart
78 lines
2.1 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:todo/sync/sync_settings.dart';
|
|
|
|
void main() {
|
|
test(
|
|
'load returns the kuhyx/todo-sync defaults on a fresh install',
|
|
() async {
|
|
SharedPreferences.setMockInitialValues({});
|
|
final s = await SyncSettings.load();
|
|
expect(s.owner, 'kuhyx');
|
|
expect(s.repo, 'todo-sync');
|
|
expect(s.token, '');
|
|
// Client id defaults to the baked-in OAuth App id (one-tap connect).
|
|
expect(s.clientId, SyncSettings.defaultClientId);
|
|
},
|
|
);
|
|
|
|
test('save then load round-trips all fields', () async {
|
|
SharedPreferences.setMockInitialValues({});
|
|
await const SyncSettings(
|
|
owner: 'me',
|
|
repo: 'notes',
|
|
token: 'tok',
|
|
clientId: 'cid',
|
|
).save();
|
|
|
|
final s = await SyncSettings.load();
|
|
expect(s.owner, 'me');
|
|
expect(s.repo, 'notes');
|
|
expect(s.token, 'tok');
|
|
expect(s.clientId, 'cid');
|
|
});
|
|
|
|
test('isConfigured requires owner, repo and token', () {
|
|
expect(
|
|
const SyncSettings(owner: 'o', repo: 'r', token: 't').isConfigured,
|
|
isTrue,
|
|
);
|
|
expect(
|
|
const SyncSettings(owner: 'o', repo: 'r', token: '').isConfigured,
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('canUseDeviceFlow needs a client id', () {
|
|
expect(
|
|
const SyncSettings(
|
|
owner: '',
|
|
repo: '',
|
|
token: '',
|
|
clientId: 'c',
|
|
).canUseDeviceFlow,
|
|
isTrue,
|
|
);
|
|
expect(
|
|
const SyncSettings(owner: '', repo: '', token: '').canUseDeviceFlow,
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('copyWith overrides only the given fields', () {
|
|
const base = SyncSettings(owner: 'o', repo: 'r', token: 't', clientId: 'c');
|
|
final next = base.copyWith(token: 'new');
|
|
expect(next.owner, 'o');
|
|
expect(next.repo, 'r');
|
|
expect(next.token, 'new');
|
|
expect(next.clientId, 'c');
|
|
|
|
// No-arg copy exercises the `?? this.x` fallback on every field.
|
|
final clone = base.copyWith();
|
|
expect(clone.owner, 'o');
|
|
expect(clone.repo, 'r');
|
|
expect(clone.token, 't');
|
|
expect(clone.clientId, 'c');
|
|
});
|
|
}
|