156 lines
16 KiB
JavaScript
156 lines
16 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
export async function run(db){
|
|
const q=(s,p=[])=>db.query(s,p);const one=async(s,p)=>(await q(s,p)).rows[0];
|
|
const ids=['00000000-0000-4000-8000-000000000001','00000000-0000-4000-8000-000000000002','00000000-0000-4000-8000-000000000003','00000000-0000-4000-8000-000000000004'];
|
|
async function actor(id,aal='aal1'){await db.exec('reset role');await q("select set_config('request.jwt.claim.sub',$1,false),set_config('request.jwt.claims',$2,false)",[id,JSON.stringify({sub:id,aal,role:'authenticated'})]);await db.exec('set role authenticated');}
|
|
async function denied(s,p,re){await assert.rejects(q(s,p),re);}
|
|
await db.exec(`create function realtime.send(jsonb,text,text,boolean) returns void language plpgsql as $$begin end$$;`);
|
|
for(let i=0;i<ids.length;i++)await q("insert into auth.users(id,email,email_confirmed_at) values($1,$2,now())",[ids[i],`test${i}@example.invalid`]);
|
|
await q('insert into public.sun_platform_admins(user_id) values($1)',[ids[0]]);
|
|
await q('insert into public.caterium_sidebar_brand_assignment(user_id) values($1)',[ids[1]]);
|
|
await denied('insert into public.caterium_sidebar_brand_assignment(user_id) values($1)',[ids[2]],/duplicate key/);
|
|
await actor(ids[1]);
|
|
assert.equal((await one('select public.caterium_my_sidebar_brand() as b')).b.variant,'solnce');
|
|
await denied('select * from public.caterium_sidebar_brand_assignment',[],/permission denied/);
|
|
await denied('update public.caterium_sidebar_brand_assignment set user_id=$1',[ids[2]],/permission denied/);
|
|
await db.exec('reset role');
|
|
await q('update auth.users set email_confirmed_at=null where id=$1',[ids[1]]);
|
|
await actor(ids[1]);assert.equal((await one('select public.caterium_my_sidebar_brand() as b')).b.variant,'caterium');
|
|
await db.exec('reset role');await q('update auth.users set email_confirmed_at=now() where id=$1',[ids[1]]);
|
|
await actor(ids[2]);assert.equal((await one('select public.caterium_my_sidebar_brand() as b')).b.variant,'caterium');
|
|
await db.exec('reset role; set role anon');await denied('select public.caterium_my_sidebar_brand()',[],/permission denied/);
|
|
await actor(ids[0]);await denied('select public.sun_dev_dashboard()',[],/AAL2/);
|
|
await actor(ids[0],'aal2');
|
|
for(let i=1;i<=2;i++)await q('select public.sun_dev_create_trial_promo($1,$2,14,7,1,\'full\',null)',[`RECOVERY-TEST-${i}`,`test${i}@example.invalid`]);
|
|
await db.exec('reset role');
|
|
for(let i=1;i<=2;i++)await q("update auth.users set raw_user_meta_data=jsonb_build_object('promo_code',$2::text) where id=$1",[ids[i],`RECOVERY-TEST-${i}`]);
|
|
await actor(ids[1]);const ws=(await one('select public.sun_create_workspace() as id')).id;
|
|
assert.equal((await q('select * from public.sun_my_workspaces()')).rows.length,1);
|
|
await denied('select public.sun_create_workspace()',[],/уже относится/);
|
|
let row=await one('select * from public.sun_fetch_app_state($1)',[ws]);
|
|
assert.deepEqual(row.payload.storage.sunBoxes?.v||[],[]);
|
|
let payload=row.payload;payload.storage.sunOrders={t:'j',v:[{id:1,client:'Test customer',date:'2099-01-01',time:'12:00',total:5000,status:'Новый'}]};payload.storage.sunBoxes={t:'j',v:[{id:'box-1',name:'Test box',price:500}]};
|
|
row=await one('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[ws,payload,'recovery-test',row.revision]);
|
|
await denied('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[ws,payload,'stale-client',row.revision-1],e=>e.code==='PT409'&&/SUN_CONFLICT/.test(e.message));
|
|
assert.equal((await one('select * from public.sun_fetch_app_state($1)',[ws])).revision,row.revision);
|
|
const snap=(await one('select public.sun_v17_entity_snapshot($1) as s',[ws])).s;
|
|
assert.equal(snap.orders.length,1);assert.equal(snap.catalog.length,1);
|
|
const client=await one('select * from public.sun_v17_save_client_v1772($1,$2,$3,null,$4)',[ws,'p:79990000000',{identity:{name:'Test client',phone:'+79990000000'}},'recovery-test']);
|
|
await denied('select * from public.sun_v17_save_client_v1772($1,$2,$3,$4,$5)',[ws,'p:79990000000',{identity:{name:'Stale overwrite'}},client.version-1,'stale-client'],e=>e.code==='PT409'&&/SUN_CLIENT_CONFLICT/.test(e.message));
|
|
const unchangedClient=await one('select * from public.sun_v17_clients_snapshot_v1773($1) where client_key=$2',[ws,'p:79990000000']);
|
|
assert.equal(unchangedClient.version,client.version);assert.equal(unchangedClient.name,'Test client');
|
|
payload.storage.sunPdfBrandNameV1='Company A';
|
|
payload.storage.sunPdfBrandLogoV1='data:image/png;base64,dGVzdA==';
|
|
row=await one('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[ws,payload,'brand-owner',row.revision]);
|
|
assert.equal((await one('select * from public.sun_fetch_app_state($1)',[ws])).payload.storage.sunPdfBrandNameV1,'Company A');
|
|
assert.equal(client.version,1);
|
|
await q('select * from public.sun_v17_clients_snapshot_v1773($1)',[ws]);
|
|
await q("select public.sun_v17_create_backup($1,'daily','test')",[ws]);
|
|
assert.equal((await q('select * from public.sun_v17_list_backups($1)',[ws])).rows.length,1);
|
|
await q('select public.sun_v17_prune_backups($1)',[ws]);
|
|
payload.storage.sunOrders.v.push({id:77,date:'2025-01-01',time:'12:00',status:'Новый',total:7500,prepayment:2000,balance:5500,autoCompletionDisabled:true});
|
|
row=await one('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[ws,payload,'import-test',row.revision]);
|
|
await q('select public.sun_run_order_automation($1)',[ws]);
|
|
const historical=(await one('select public.sun_v17_entity_snapshot($1) as s',[ws])).s.orders.find(o=>String(o.id)==='77').data;
|
|
assert.equal(historical.prepayment,2000);assert.equal(historical.status,'Новый');assert.equal(historical.balance,5500);
|
|
await q('select public.caterium_account_snapshot($1)',[ws]);
|
|
await q('select * from public.sun_list_workspace_members($1)',[ws]);
|
|
await q("select public.sun_employee_prepare_v28($1,'test3@example.invalid','Viewer','viewer')",[ws]);
|
|
await q("select public.sun_employee_finalize_v28($1,$2,'Viewer','viewer')",[ws,ids[3]]);
|
|
await actor(ids[2]);const foreign=(await one('select public.sun_create_workspace() as id')).id;
|
|
assert.equal((await one('select * from public.sun_fetch_app_state($1)',[foreign])).payload.storage.sunPdfBrandLogoV1,undefined);
|
|
const demoRow=await one('select * from public.sun_fetch_app_state($1)',[foreign]);
|
|
const training=demoRow.payload.storage;
|
|
assert.equal(training.sunBoxes.v.length,44);assert.equal(training.sunStock.v.length,63);assert.equal(training.sunSuppliers.v.length,5);
|
|
assert.deepEqual([0,1,2,3,4,5,6].map(c=>training.sunBoxes.v.filter(b=>b.category===c).length),[10,4,3,4,2,3,18]);
|
|
assert.equal(training.sunBoxes.v.filter(b=>b.category===6).length,18);assert.equal(training.sunTrialDemoV1.v.banquetVersion,1);
|
|
assert.equal(training.sunOrders,undefined);assert.equal(training.sunTrialDemoV1.v.version,1);
|
|
assert.equal((await one('select public.sun_v17_entity_snapshot($1) as s',[foreign])).s.catalog.length,44);
|
|
assert.ok(training.sunStockMoves.v.length>0);assert.ok(training.sunStock.v.some(p=>p.qty===0));
|
|
for(const b of training.sunBoxes.v){for(const [name,qty,unit] of b.ingredients){assert.ok(qty>0);assert.ok(training.sunStock.v.some(p=>p.name===name&&p.unit===unit));}if(!b.ttk)continue;assert.ok(b.ttk.ingredientCost>0);assert.equal(b.ttk.outputGrams,Math.round(b.ttk.rows.filter(r=>r.unit==='кг').reduce((s,r)=>s+r.net*1000,b.ttk.waterGrams||0)));for(const r of b.ttk.rows){assert.ok(r.gross>=r.net);assert.ok(training.sunStock.v.some(p=>p.id===r.productId&&p.name===r.name&&p.unit===r.unit&&p.cost===r.unitCost));}}
|
|
await q('select public.caterium_install_trial_demo($1)',[foreign]);
|
|
assert.equal((await one('select * from public.sun_fetch_app_state($1)',[foreign])).revision,demoRow.revision,'idempotent demo install does not overwrite');
|
|
// Model an already used v1 trial: no banquet yet, edited prices, real balances and an order.
|
|
const legacy=structuredClone(demoRow.payload);
|
|
legacy.storage.sunBoxes.v=legacy.storage.sunBoxes.v.filter(b=>b.category===0);
|
|
legacy.storage.sunBoxes.v[0].price=4321;
|
|
legacy.storage.sunStock.v=legacy.storage.sunStock.v.filter(p=>p.id.startsWith('demo-v1-stock-'));
|
|
legacy.storage.sunStock.v[0].qty=7.123;
|
|
delete legacy.storage.sunTrialDemoV1.v.banquetVersion;
|
|
delete legacy.storage.sunTrialDemoV1.v.extrasVersion;
|
|
legacy.storage.sunOrders={t:'j',v:[{id:91,event:'Keep my order',date:'2099-12-20',time:'12:00',status:'Новый',lines:[],total:4321,prepayment:100,balance:4221,autoCompletionDisabled:true}]};
|
|
let upgraded=await one('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[foreign,legacy,'legacy-trial-fixture',demoRow.revision]);
|
|
assert.equal((await one('select public.caterium_trial_demo_status($1) as s',[foreign])).s.canUpgrade,true);
|
|
await q('select public.caterium_install_trial_demo($1)',[foreign]);
|
|
upgraded=await one('select * from public.sun_fetch_app_state($1)',[foreign]);
|
|
assert.deepEqual(upgraded.payload.storage.sunBoxes.v.slice(0,10),legacy.storage.sunBoxes.v);
|
|
assert.deepEqual(upgraded.payload.storage.sunStock.v.slice(0,43),legacy.storage.sunStock.v);
|
|
for(const key of ['sunOrders','sunStockMoves','sunSuppliers'])assert.deepEqual(upgraded.payload.storage[key],legacy.storage[key]);
|
|
assert.equal(upgraded.payload.storage.sunBoxes.v.length,44);
|
|
await q('select public.caterium_install_trial_banquet($1)',[foreign]);
|
|
assert.equal((await one('select * from public.sun_fetch_app_state($1)',[foreign])).revision,upgraded.revision);
|
|
// Existing custom drink/ware tabs must remain untouched, with no unused demo stock.
|
|
const partial=structuredClone(upgraded.payload);
|
|
partial.storage.sunBoxes.v=partial.storage.sunBoxes.v.filter(b=>!b.id.startsWith('demo-extras-'));
|
|
partial.storage.sunStock.v=partial.storage.sunStock.v.filter(p=>!p.id.startsWith('demo-extras-'));
|
|
delete partial.storage.sunTrialDemoV1.v.extrasVersion;
|
|
const custom=[{id:'own-drink',name:'My drink',category:3,price:123,ingredients:[]},{id:'own-plate',name:'My plate',category:1,price:456,ingredients:[]}];
|
|
partial.storage.sunBoxes.v.push(...custom);
|
|
await q('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[foreign,partial,'partly-filled-trial',upgraded.revision]);
|
|
await q('select public.caterium_install_trial_demo($1)',[foreign]);
|
|
upgraded=await one('select * from public.sun_fetch_app_state($1)',[foreign]);
|
|
assert.equal(upgraded.payload.storage.sunBoxes.v.length,38);
|
|
assert.deepEqual(upgraded.payload.storage.sunBoxes.v.filter(b=>b.category===1||b.category===3),custom);
|
|
assert.equal(upgraded.payload.storage.sunStock.v.length,55);
|
|
assert.deepEqual(upgraded.payload.storage.sunBoxes.v.slice(0,30),partial.storage.sunBoxes.v);
|
|
assert.deepEqual(upgraded.payload.storage.sunStock.v.slice(0,52),partial.storage.sunStock.v);
|
|
assert.equal(upgraded.payload.storage.sunTrialDemoV1.v.banquetInstalledAt,partial.storage.sunTrialDemoV1.v.banquetInstalledAt,'already upgraded banquet untouched');
|
|
await q('select public.caterium_install_trial_extras($1)',[foreign]);
|
|
assert.equal((await one('select * from public.sun_fetch_app_state($1)',[foreign])).revision,upgraded.revision);
|
|
const filled=structuredClone(upgraded.payload);delete filled.storage.sunTrialDemoV1;
|
|
await q('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[foreign,filled,'test-existing-catalog',upgraded.revision]);
|
|
await denied('select public.caterium_install_trial_banquet($1)',[foreign],/Сначала установите/);
|
|
await denied('select public.caterium_install_trial_extras($1)',[foreign],/Сначала установите/);
|
|
await denied('select public.caterium_install_trial_demo($1)',[foreign],/уже заполнены/);
|
|
assert.equal((await one('select public.caterium_my_sidebar_brand() as b')).b.variant,'caterium');
|
|
await denied('select * from public.sun_fetch_app_state($1)',[ws],/Access denied/);
|
|
await denied('select public.caterium_trial_demo_status($1)',[ws],/Access denied/);
|
|
await denied('select public.caterium_install_trial_demo($1)',[ws],/Administrator membership/);
|
|
await denied('select public.caterium_install_trial_banquet($1)',[ws],/Administrator membership/);
|
|
await denied('select public.caterium_install_trial_extras($1)',[ws],/Administrator membership/);
|
|
await denied('select * from public.sun_v17_clients_snapshot_v1773($1)',[ws],/Access denied/);
|
|
await denied('select public.sun_dev_dashboard()',[],/administrator/);
|
|
await actor(ids[3]);
|
|
await denied('select public.caterium_install_trial_demo($1)',[ws],/Administrator membership/);
|
|
assert.equal((await one('select public.caterium_my_sidebar_brand() as b')).b.variant,'caterium');
|
|
await denied('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[ws,{...payload,storage:{...payload.storage,sunPdfBrandNameV1:'Hijacked'}},'viewer-brand',row.revision],/Нет права/);
|
|
await denied('select * from public.sun_save_app_state_v17($1,$2,$3,$4)',[ws,{...payload,storage:{...payload.storage,sunOrders:{t:'j',v:[{id:2}]}}},'viewer',row.revision],/Нет права/);
|
|
await actor(ids[0],'aal2');
|
|
await denied('select public.caterium_install_trial_demo($1)',[foreign],/Administrator membership/);
|
|
for(const f of ['sun_dev_dashboard','sun_dev_list_companies','sun_dev_list_companies_v1768','sun_dev_list_users','sun_dev_list_errors','sun_dev_list_activity','sun_dev_list_trial_promos'])await q(`select * from public.${f}()`);
|
|
for(const f of ['sun_dev_workspace_diagnostics','sun_dev_list_workspace_features','sun_dev_support_snapshot'])await q(`select * from public.${f}($1)`,[ws]);
|
|
await q("select public.sun_dev_create_company('Recovery test company','test3@example.invalid','full',30,'[]','test')");
|
|
await actor(ids[1]);
|
|
assert.equal((await one('select public.caterium_trial_demo_status($1) as s',[ws])).s.eligible,false,'Solnce company excluded');
|
|
await denied('select public.caterium_install_trial_demo($1)',[ws],/Демонстрационная база доступна/);
|
|
await denied('select public.caterium_install_trial_banquet($1)',[ws],/Демонстрационная база доступна/);
|
|
await denied('select public.caterium_install_trial_extras($1)',[ws],/Демонстрационная база доступна/);
|
|
await db.exec('reset role');
|
|
const devWs=(await one("insert into public.sun_workspaces(name,created_by) values('Own developer test',$1) returning id",[ids[0]])).id;
|
|
await q("insert into public.sun_workspace_members(workspace_id,user_id,role,is_active,permissions) values($1,$2,'admin',true,public.sun_role_default_permissions('admin'))",[devWs,ids[0]]);
|
|
await q("insert into public.sun_workspace_subscriptions(workspace_id,plan_id,status,current_period_end) values($1,'full','active',now()+interval '30 days')",[devWs]);
|
|
const preserved={format:'sun-cloud-v2',version:2,storage:{sunCompanyProfileV1:{t:'j',v:{name:'Own company'}},sunClientLoyaltyV1:{t:'j',v:{fixture:'preserve me'}}}};
|
|
await q("insert into public.sun_app_state(workspace_id,payload,client_id) values($1,$2,'fixture')",[devWs,preserved]);
|
|
await actor(ids[0]);
|
|
await q('select public.caterium_install_trial_demo($1)',[devWs]);
|
|
const seeded=(await one('select * from public.sun_fetch_app_state($1)',[devWs])).payload.storage;
|
|
assert.deepEqual(seeded.sunClientLoyaltyV1,preserved.storage.sunClientLoyaltyV1);assert.deepEqual(seeded.sunCompanyProfileV1,preserved.storage.sunCompanyProfileV1);
|
|
assert.equal(seeded.sunBoxes.v.length,44,'developer own company works without developer-panel MFA');
|
|
await db.exec('reset role; set role anon');await denied('select public.caterium_install_trial_extras($1)',[devWs],/permission denied/);await denied('select public.caterium_trial_extras_seed_v1()',[],/permission denied/);
|
|
await db.exec('reset role; set role anon');await denied('select public.caterium_install_trial_banquet($1)',[devWs],/permission denied/);await denied('select public.caterium_trial_banquet_seed_v1()',[],/permission denied/);
|
|
await db.exec('reset role; set role anon');await denied('select public.caterium_install_trial_demo($1)',[devWs],/permission denied/);await denied('select public.caterium_trial_demo_seed_v1()',[],/permission denied/);
|
|
console.log('PASS demo: trial auto seed, 10 box + 18 banquet TTKs, stock links, additive upgrade preserving orders/prices/balances, idempotency, populated catalog guard, Solnce exclusion, own developer account and preserved clients');
|
|
console.log('PASS smoke: owner onboarding, save/read, optimistic conflict, normalized catalog/orders/clients, backups, employees, cross-tenant isolation, viewer write denial, AAL2, developer RPCs');
|
|
await db.close();
|
|
}
|