aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/harness/photo_sync_probe.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-10 14:21:46 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-10 14:21:46 +0200
commit2f2a9a6542d2194e50ffba6f382e4fdffd481838 (patch)
tree5437330f9e8b963f8af0904f3c5d80d1afc6dc3b /packages/meshbay-hub/tests/harness/photo_sync_probe.py
parent914d2f0af848fb80d70d517901f44420a4e47764 (diff)
downloadmeshbay-2f2a9a6542d2194e50ffba6f382e4fdffd481838.tar.gz
feat(android): one backup destination, a group the account owns alone
Chosen once at the top of Android Sync for every kind; photos and contacts go into <folder>/<account>-photos and -contacts. Owner and sole member are checked at set-up and before every run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests/harness/photo_sync_probe.py')
-rw-r--r--packages/meshbay-hub/tests/harness/photo_sync_probe.py40
1 files changed, 29 insertions, 11 deletions
diff --git a/packages/meshbay-hub/tests/harness/photo_sync_probe.py b/packages/meshbay-hub/tests/harness/photo_sync_probe.py
index 08c21b9..c1c4599 100644
--- a/packages/meshbay-hub/tests/harness/photo_sync_probe.py
+++ b/packages/meshbay-hub/tests/harness/photo_sync_probe.py
@@ -8,8 +8,9 @@ Only the Android application has this section, and its engine is Chromium, so
one engine is the whole of it. The bridge is a stand-in that answers the way
the phone does; the group connection (`connection-pool.js`) is replaced in the
copy by one that hands out a node with long, space-free folder names, because
-those are what widen a page (CLAUDE.md). Three states: setting up, set up with
-a refusal showing, and setting up with only selected photos allowed.
+those are what widen a page (CLAUDE.md). Four states: choosing where backups
+go, setting up photos, set up with a refusal showing, and setting up with only
+selected photos allowed.
photo_sync_probe.py prints one JSON object, keyed <state><width>
"""
@@ -61,16 +62,20 @@ window.addEventListener('error', (e) => note(e.message));
window.addEventListener('unhandledrejection',
(e) => note((e.reason && e.reason.message) || e.reason));
const MODE = location.hash.slice(1);
-const cfg = {
- account: 'bob', groupId: 'g1', groupName: '__GROUP__', owner: '__OWNER__',
- folder: '__FOLDER__', albums: ['cam'], includeExisting: true, since: 0,
-};
+const dest = MODE === 'dest' ? null
+ : { account: 'bob', groupId: 'g1', groupName: '__GROUP__', folder: '__FOLDER__' };
+const cfg = { albums: ['cam'], includeExisting: true, since: 0 };
window.meshbay = {
hubBase: () => 'https://hub.invalid',
+ phoneSync: {
+ destination: async () => dest,
+ setDestination: async (d) => d,
+ },
photoSync: {
status: async () => ({
permission: MODE === 'partial' ? 'partial' : 'granted', unmetered: true, sent: 1240,
config: MODE === 'configured' ? cfg : null, lastCompleted: Date.now(),
+ destination: dest, dir: dest && '__FOLDER__/bob-photos',
failure: MODE === 'configured' ? 'disk_full' : null,
}),
albums: async () => [
@@ -83,6 +88,15 @@ window.meshbay = {
sent: async () => true, completed: async () => ({}), failed: async () => true,
keepAlive: async () => true,
},
+ contactSync: {
+ status: async () => ({
+ permission: 'granted', on: MODE === 'configured', destination: dest,
+ dir: dest && '__FOLDER__/bob-contacts', sent: 3, lastCompleted: Date.now(),
+ lastSent: dest && '__FOLDER__/bob-contacts/contacts-2026-10-10-0900.vcf', failure: null,
+ }),
+ permit: async () => ({}), configure: async () => ({}), plan: async () => ({ item: null }),
+ sent: async () => true, completed: async () => ({}), failed: async () => true,
+ },
};
</script>
<main class="main"><div id=root></div></main>
@@ -92,14 +106,18 @@ import { initLocale } from './i18n.js';
import { startPhotoSync } from './photo-sync.js';
import { AndroidSyncPage } from './android-sync-page.js';
await initLocale();
-startPhotoSync({ hub: '', getUser: () => ({ username: 'bob', token: 't' }),
- getBundleKey: async () => '' });
+startPhotoSync({
+ hub: '', getUser: () => ({ username: 'bob', userId: 'u-bob', token: 't' }),
+ hubFetch: async () => ({ admin_id: 'u-bob', members: [{ user_id: 'u-bob', username: 'bob' }] }),
+ getBundleKey: async () => '' });
const groups = [{ id: 'g1', name: '__GROUP__', owner_username: '__OWNER__' }];
-render(html`<${AndroidSyncPage} user=${{ username: 'bob', token: 't' }} groups=${groups} />`,
+render(html`<${AndroidSyncPage} user=${{ username: 'bob', userId: 'u-bob', token: 't' }}
+ groups=${groups} />`,
document.getElementById('root'));
setTimeout(() => {
if (MODE === 'configured') return;
[...document.querySelectorAll('button')].find((b) => b.textContent.includes('Set up')).click();
+ if (MODE !== 'dest') return;
setTimeout(() => {
const sel = document.querySelector('select');
sel.value = 'g1';
@@ -112,7 +130,7 @@ setTimeout(() => {
OUTER = """<!doctype html><html><body style="margin:0">
<script>
-for (const m of ['setup', 'configured', 'partial']) for (const w of [360, 420]) {
+for (const m of ['dest', 'setup', 'configured', 'partial']) for (const w of [360, 420]) {
const f = document.createElement('iframe');
f.id = m + w;
f.style.cssText = `width:${w}px;height:900px;border:0;display:block`;
@@ -129,7 +147,7 @@ setTimeout(() => {
.map((e) => `${e.tagName}.${e.className} ${Math.round(e.getBoundingClientRect().right)}`);
out[f.id] = {
vw, scrollW: d.documentElement.scrollWidth, wide: wide.slice(0, 6),
- text: d.querySelector('#root').innerText.slice(0, 600),
+ text: d.querySelector('#root').innerText.slice(0, 3000),
err: d.documentElement.dataset.err || '',
checked: [...d.querySelectorAll('#root input:checked')]
.map((i) => i.closest('label').innerText.split('\\n')[0]),