| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- const base = 'http://localhost:3101/api';
- const results = [];
- function pass(name, detail = '') {
- results.push({ name, ok: true, detail });
- console.log(`[PASS] ${name}${detail ? ' - ' + detail : ''}`);
- }
- function fail(name, detail = '') {
- results.push({ name, ok: false, detail });
- console.log(`[FAIL] ${name}${detail ? ' - ' + detail : ''}`);
- }
- async function login(phone) {
- const r = await fetch(`${base}/auth/login`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ phone, password: '123456', rememberMe: false }),
- });
- const j = await r.json();
- if (!j.success) throw new Error(`login ${email}: ${JSON.stringify(j.error)}`);
- return j.data;
- }
- async function get(path, token) {
- const r = await fetch(`${base}${path}`, {
- headers: token ? { Authorization: `Bearer ${token}` } : {},
- });
- let j = null;
- try { j = await r.json(); } catch { j = null; }
- return { status: r.status, j };
- }
- async function post(path, token, body) {
- const r = await fetch(`${base}${path}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
- },
- body: body ? JSON.stringify(body) : undefined,
- });
- let j = null;
- try { j = await r.json(); } catch { j = null; }
- return { status: r.status, j };
- }
- console.log('\n=== Auto API Test Suite ===\n');
- // 1. Health
- try {
- const h = await get('/health');
- if (h.j?.success && h.j.data?.status === 'ok') {
- pass('health check', `groups=${h.j.data.database?.counts?.groupChat ?? '?'}`);
- } else fail('health check', JSON.stringify(h.j));
- } catch (e) {
- fail('health check', e.message);
- }
- // 2. Auth required
- try {
- const g = await get('/qiwe/groups');
- if (g.status === 401) pass('groups without auth returns 401');
- else fail('groups without auth returns 401', `status=${g.status}`);
- } catch (e) {
- fail('groups without auth returns 401', e.message);
- }
- // 3. Logins
- let director, supervisor, manager;
- try {
- director = await login('13800000001');
- pass('login director', director.user.email);
- } catch (e) { fail('login director', e.message); }
- try {
- supervisor = await login('13800000002');
- pass('login supervisor', `region=${supervisor.user.regionCode}`);
- } catch (e) { fail('login supervisor', e.message); }
- try {
- manager = await login('13800000003');
- pass('login store_manager', `storeId=${manager.user.storeId}`);
- } catch (e) { fail('login store_manager', e.message); }
- if (!director?.token) {
- console.log('\n=== Summary: aborted (no director token) ===');
- process.exit(1);
- }
- const t = director.token;
- // 4. Director scope
- const dirDefault = await get('/dashboard/overview', t);
- if (dirDefault.j?.data?.scope?.level === 'city') {
- pass('director default scope is city', dirDefault.j.data.scope.label || '');
- } else fail('director default scope is city', dirDefault.j?.data?.scope?.level);
- const dirGlobal = await get('/dashboard/overview?scopeLevel=global', t);
- if (dirGlobal.j?.data?.scope?.level === 'global') {
- pass('director can view global overview');
- } else fail('director can view global overview', dirGlobal.j?.data?.scope?.level);
- // 5. Supervisor scope
- if (supervisor?.token) {
- const supGlobal = await get('/dashboard/overview?scopeLevel=global', supervisor.token);
- if (supGlobal.status === 403 || supGlobal.j?.error?.code === 'SCOPE_DENIED') {
- pass('supervisor blocked from global');
- } else fail('supervisor blocked from global', `status=${supGlobal.status}`);
- const supRegion = await get('/dashboard/overview?scopeLevel=region', supervisor.token);
- if (supRegion.j?.data?.scope?.level === 'region') {
- pass('supervisor region scope', supRegion.j.data.scope.regionCode);
- } else fail('supervisor region scope', supRegion.j?.data?.scope?.level);
- }
- // 6. Manager scope
- if (manager?.token) {
- const mgrGlobal = await get('/dashboard/overview?scopeLevel=global', manager.token);
- if (mgrGlobal.status === 403 || mgrGlobal.j?.error?.code === 'SCOPE_DENIED') {
- pass('manager blocked from global');
- } else fail('manager blocked from global', `status=${mgrGlobal.status}`);
- }
- // 7. Groups by scope
- const gGlobal = await get('/qiwe/groups?scopeLevel=global', t);
- const gStore = await get('/qiwe/groups', t);
- const globalCount = gGlobal.j?.data?.total ?? 0;
- const storeCount = gStore.j?.data?.total ?? 0;
- pass('groups global count', String(globalCount));
- pass('groups default store count', String(storeCount));
- if (globalCount >= storeCount) {
- pass('global groups >= store groups');
- } else fail('global groups >= store groups', `${globalCount} vs ${storeCount}`);
- // 8. Group fields
- const sample = gGlobal.j?.data?.groups?.[0];
- if (sample?.healthGrade && sample?.lifecyclePhase) {
- pass('group has healthGrade and lifecyclePhase', `${sample.healthGrade}/${sample.lifecyclePhase}`);
- } else if (globalCount === 0) {
- pass('group fields skipped', 'no groups in db');
- } else {
- fail('group has healthGrade and lifecyclePhase', JSON.stringify(sample));
- }
- // 9. Communities
- const comm = await get('/qiwe/communities', t);
- const commTotal = comm.j?.data?.total ?? 0;
- if (commTotal > 0) pass('communities list', `total=${commTotal}`);
- else fail('communities list', 'total=0');
- // 10. Dashboard lifecycle distribution
- const life = dirDefault.j?.data?.lifecycleDistribution;
- if (Array.isArray(life) && life.length >= 4) {
- pass('dashboard lifecycleDistribution', `phases=${life.length}`);
- } else fail('dashboard lifecycleDistribution', String(life?.length));
- // 11. QiWe sync-groups
- const sync = await post('/qiwe/sync-groups');
- if (sync.j?.success) {
- const s = sync.j.data?.sync;
- pass('sync-groups API', `created=${s?.created} updated=${s?.updated} errors=${s?.errors?.length ?? 0}`);
- } else {
- const msg = sync.j?.error?.message || sync.status;
- if (String(msg).includes('guid') || String(msg).includes('不在线')) {
- fail('sync-groups API', `QiWe offline: ${msg}`);
- } else {
- fail('sync-groups API', msg);
- }
- }
- // 12. QiWe sync-messages
- const msgSync = await post('/qiwe/sync-messages', null, { limit: 20, maxPages: 3 });
- if (msgSync.j?.success) {
- const s = msgSync.j.data?.sync;
- const dbMsg = msgSync.j.data?.database?.counts?.message;
- pass('sync-messages API', `created=${s?.created} skipped=${s?.skipped} total=${dbMsg}`);
- } else {
- const msg = msgSync.j?.error?.message || msgSync.status;
- if (String(msg).includes('不在线') || String(msg).includes('QiWe')) {
- fail('sync-messages API', `QiWe: ${msg}`);
- } else {
- fail('sync-messages API', msg);
- }
- }
- // 13. Messages list (if groups exist)
- const sampleRoom = gGlobal.j?.data?.groups?.[0]?.roomId;
- if (sampleRoom) {
- const msgs = await get(`/qiwe/messages?roomId=${encodeURIComponent(sampleRoom)}&limit=5`, t);
- if (msgs.j?.success && Array.isArray(msgs.j.data?.messages)) {
- pass('messages list API', `room=${sampleRoom} total=${msgs.j.data.total}`);
- } else {
- fail('messages list API', JSON.stringify(msgs.j?.error));
- }
- } else if (globalCount === 0) {
- pass('messages list API skipped', 'no groups');
- } else {
- fail('messages list API', 'no sample room');
- }
- // 14. Org sync
- const org = await post('/qiwe/org/sync', t);
- if (org.j?.success) {
- pass('org sync', `members=${org.j.data?.members} source=${org.j.data?.source}`);
- } else {
- fail('org sync', org.j?.error?.message || org.status);
- }
- // 15. Risk API not implemented yet (expected 404)
- const risk = await get('/risk/work-orders', t);
- if (risk.status === 404) {
- pass('risk API not implemented (404 expected)', 'frontend still uses mock');
- } else if (risk.j?.success) {
- pass('risk work-orders API exists', `total=${risk.j.data?.total ?? '?'}`);
- } else {
- pass('risk API response', `status=${risk.status}`);
- }
- // Summary
- console.log('\n=== Summary ===');
- const passed = results.filter((r) => r.ok).length;
- const failed = results.filter((r) => !r.ok);
- console.log(`Passed: ${passed}/${results.length}, Failed: ${failed.length}`);
- if (failed.length) {
- console.log('\nFailed tests:');
- failed.forEach((f) => console.log(` - ${f.name}: ${f.detail}`));
- process.exit(1);
- }
- process.exit(0);
|