Update shared Gaia workflows and Sargo development tooling

Fix camera switching and Contacts/SMS activity routing across shared hosts.
Integrate Marketplace installation and native standalone recovery tools,
Bluetooth runtime packages and trusted certificates.

Update English/French READMEs and the shared changelog with tested scope
and remaining radio/audio/Bluetooth limitations. Preview17 remains the
existing APK; no new native image is released.

Validation: shared JavaScript and native Python tests; Android packaging
and payload parity from an isolated publication checkout.
This commit is contained in:
OverSu 2026-10-08 14:27:17 +02:00
commit 0041cd9660
101 changed files with 3883 additions and 335 deletions

59
tests/contacts.test.mjs Normal file
View file

@ -0,0 +1,59 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {Contacts} from '../host/Contacts.sys.mjs';
let serial=0;
globalThis.Services={uuid:{generateUUID:()=>({toString:()=>`test-${++serial}`})}};
const writer={manifest:{permissions:{contacts:{access:'readwrite'}}}};
const reader={manifest:{permissions:{contacts:{access:'readonly'}}}};
async function addressBook() {
let stored;
const events=[];
const storage={async run(_store,operation,args) {
if(operation==='get') return {value:structuredClone(stored)};
stored=structuredClone(args[0]);
}};
const contacts=new Contacts(storage,event=>events.push(event));
await contacts.init();
return {contacts,events,storage};
}
test('contact edits, photo, date and favourite survive reopening the store',async()=>{
const {contacts,events,storage}=await addressBook();
const first=await contacts.request(writer,{operation:'save',contact:{
givenName:['Élodie'],familyName:['Test'],tel:[{value:'+33 6 00 00 00 00'}],
photo:[new Blob(['portrait'],{type:'image/jpeg'})],bday:new Date('1995-08-19T00:00:00Z'),
}});
const edited=await contacts.request(writer,{operation:'save',contact:{...first,familyName:['Modifiée'],category:['favorite']}});
assert.equal(edited.id,first.id);
assert.equal(edited.published,first.published);
const reopened=new Contacts(storage,()=>{});await reopened.init();
const [row]=await reopened.request(reader,{operation:'find',options:{filterBy:['id'],filterOp:'equals',filterValue:first.id}});
assert.deepEqual(row.familyName,['Modifiée']);
assert.deepEqual(row.category,['favorite']);
assert.equal(await row.photo[0].text(),'portrait');
assert.equal(row.bday.toISOString(),'1995-08-19T00:00:00.000Z');
assert.deepEqual(events.map(e=>e.reason),['create','update']);
await contacts.request(writer,{operation:'remove',id:first.id});
assert.equal(await contacts.request(reader,{operation:'count'}),0);
});
test('a failed write emits no contactchange and does not poison later saves',async()=>{
const {contacts,events,storage}=await addressBook();
const run=storage.run;
storage.run=async()=>{throw Error('disk full');};
await assert.rejects(contacts.request(writer,{operation:'save',contact:{name:['First']}}),/disk full/);
assert.equal(events.length,0);
assert.equal(await contacts.request(reader,{operation:'count'}),0);
storage.run=run;
await contacts.request(writer,{operation:'save',contact:{name:['Second']}});
assert.equal(await contacts.request(reader,{operation:'count'}),1);
});
test('read-only callers cannot change the address book',async()=>{
const {contacts}=await addressBook();
for(const operation of ['save','remove','clear'])
await assert.rejects(contacts.request(reader,{operation,contact:{name:['Denied']},id:'anything'}),/PERMISSION_DENIED/);
await assert.rejects(contacts.request({manifest:{}},{operation:'find'}),/PERMISSION_DENIED/);
});