78 lines
3.3 KiB
Dart
78 lines
3.3 KiB
Dart
// Cross-client interop: kirakira's gsmol export imports into fumi, and
|
|
// fumi's export imports back into kirakira — the interchange the export
|
|
// patch upstream exists for. The kirakira side is driven through the real
|
|
// store API now: pins and contacts (with rotation history) land in the
|
|
// native store, and the sealed mail round-trip is covered by fumi-core's
|
|
// own export tests, so this proves the container compatibility both ways.
|
|
// Runs the fumi CLI as a subprocess, so the test skips when no built fumi
|
|
// sits in the sibling checkout.
|
|
|
|
import "dart:io";
|
|
import "dart:typed_data";
|
|
|
|
import "package:flutter_test/flutter_test.dart";
|
|
import "package:hive_flutter/hive_flutter.dart";
|
|
|
|
import "package:smol_mail/smol/address.dart";
|
|
import "package:smol_mail/smol/store.dart";
|
|
|
|
const fumiBinary = "../fumi/target/release/fumi";
|
|
|
|
void main() {
|
|
final fumiBuilt = File(fumiBinary).existsSync();
|
|
|
|
setUpAll(() async {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
final dir = await Directory.systemTemp.createTemp("smol-interop");
|
|
Hive.init(dir.path);
|
|
});
|
|
|
|
test("exports cross-import in both directions",
|
|
skip: fumiBuilt ? false : "no built fumi in ../fumi", () async {
|
|
final dir = await Directory.systemTemp.createTemp("fumi-interop");
|
|
final master = Uint8List.fromList(List.generate(32, (i) => i + 3));
|
|
// Real, distinct, valid key forms: the system server's and fumi's.
|
|
const pinKey = "lm2gqd7e5q67xq3isc5hx6jfj2q7a7xvq4l7trctxipudujovmgq";
|
|
|
|
final mine = await SmolStore.open(
|
|
dbPath: "${dir.path}/kirakira.db",
|
|
stateBox: "interop-state",
|
|
readBox: "interop-read");
|
|
mine.restoreMaster(master, 0);
|
|
mine.pinServer("example.org", pinKey, defaultPort);
|
|
// One contact, bound to the current key; rotation history is written by
|
|
// the trust engine on a validated rotation, not by a plain re-save.
|
|
await mine.saveContact("alice@example.org", pinKey, verified: true);
|
|
|
|
final file = File("${dir.path}/kirakira.json");
|
|
await file.writeAsString(await mine.exportData());
|
|
|
|
// fumi imports it.
|
|
final keyFile = File("${dir.path}/identity.key");
|
|
await keyFile.writeAsBytes(master);
|
|
Future<ProcessResult> fumi(List<String> args) => Process.run(
|
|
fumiBinary,
|
|
["--key", keyFile.path, "--db", "${dir.path}/fumi.db", ...args]);
|
|
final into = await fumi(["import-backup", file.path]);
|
|
expect(into.exitCode, 0, reason: into.stderr.toString());
|
|
expect(into.stdout.toString(), contains("1 contacts"));
|
|
|
|
// fumi exports its store, and kirakira imports that back.
|
|
final fumiFile = File("${dir.path}/fumi.json");
|
|
final out = await fumi(["export", fumiFile.path]);
|
|
expect(out.exitCode, 0, reason: out.stderr.toString());
|
|
final restored = await SmolStore.open(
|
|
dbPath: "${dir.path}/restored.db",
|
|
stateBox: "interop2-state",
|
|
readBox: "interop2-read");
|
|
restored.restoreMaster(master, 0);
|
|
final summary = await restored.importData(await fumiFile.readAsString());
|
|
expect(summary.contactsAdded, 1);
|
|
expect(summary.pinsAdded, 1);
|
|
expect(summary.malformed, 0);
|
|
// The binding survived both directions of the round trip.
|
|
final contact = restored.contact("alice@example.org")!;
|
|
expect(contact.key, pinKey);
|
|
expect(restored.serverPin("example.org", defaultPort), pinKey);
|
|
});
|
|
}
|