This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { Hono } from 'hono';
|
||||
import pool from '../../db.js';
|
||||
import type { AuthUser } from '../../auth/types.js';
|
||||
import type { MonitoringResponse } from './types.js';
|
||||
import monitoring from './monitoring.js';
|
||||
import recent from './vehicle-recent.js';
|
||||
|
||||
const originalConfig = { url: process.env.ONEOS_MILEAGE_API_BASE_URL, key: process.env.ONEOS_MILEAGE_API_KEY };
|
||||
test.before(() => { process.env.ONEOS_MILEAGE_API_BASE_URL = 'https://oneos.test'; process.env.ONEOS_MILEAGE_API_KEY = 'test-only'; });
|
||||
test.after(() => {
|
||||
if (originalConfig.url === undefined) delete process.env.ONEOS_MILEAGE_API_BASE_URL; else process.env.ONEOS_MILEAGE_API_BASE_URL = originalConfig.url;
|
||||
if (originalConfig.key === undefined) delete process.env.ONEOS_MILEAGE_API_KEY; else process.env.ONEOS_MILEAGE_API_KEY = originalConfig.key;
|
||||
});
|
||||
|
||||
test('summary integration preserves scope, valid partial mileage, trend nulls, reuse, details and refresh', async t => {
|
||||
const requests: {url:string;body:any}[]=[];
|
||||
let reassigned=false;
|
||||
let failure=0;
|
||||
t.mock.method(globalThis,'fetch',async(url:string,options:RequestInit)=>{
|
||||
const body=JSON.parse(options.body as string); requests.push({url,body});
|
||||
if(url.endsWith('/summary/query')){
|
||||
if(failure)return Response.json({code:'UPSTREAM_ERROR',traceId:'trace'}, {status:failure,headers:{'Retry-After':'12'}});
|
||||
const selected=body.plateNumbers || ['TEST0','TEST1','TEST2'];
|
||||
const values=[0,30,20];
|
||||
return Response.json({code:'SUCCESS',startDate:body.startDate,endDate:body.endDate,timezone:'Asia/Shanghai',vehicleCount:selected.length,
|
||||
data:{vehicles:selected.map((plate:string)=>({plateNumber:plate,vin:'VIN',periodMileageKm:values[Number(plate.slice(-1))],
|
||||
endDateTotalMileageKm:plate==='TEST0'?null:100,endDateDataTime:null,endDateSourceProtocol:null,
|
||||
validDays:1,missingDays:plate==='TEST2'?0:1,anomalyDays:plate==='TEST2'?1:0,
|
||||
status:plate==='TEST2'?'DATA_ANOMALY':'PARTIAL_DATA',qualityReasons:[plate==='TEST2'?'BASELINE_MISSING':'CARRIED_FORWARD'],sourceProtocols:['MQTT'],updatedAt:null})),
|
||||
dailyTrend:[{date:body.startDate,mileageKm:selected.reduce((sum:number,p:string)=>sum+values[Number(p.slice(-1))],0),validVehicleCount:selected.length,missingVehicleCount:0,anomalyVehicleCount:0},
|
||||
{date:body.endDate,mileageKm:null,validVehicleCount:0,missingVehicleCount:selected.filter((p:string)=>p!=='TEST2').length,anomalyVehicleCount:selected.includes('TEST2')?1:0}]},
|
||||
snapshotId:'snapshot',generatedAt:'2026-10-27T00:00:00Z',expiresAt:'2099-01-01T00:00:00Z',nextCursor:null});
|
||||
}
|
||||
if(url.endsWith('/range/query'))return Response.json({code:'SUCCESS',data:(body.plateNumbers||[]).flatMap((plate:string)=>[
|
||||
{plateNumber:plate,date:body.startDate,dailyMileageKm:20,totalMileageKm:100,status:'NORMAL',sourceProtocol:'MQTT'},
|
||||
{plateNumber:plate,date:body.endDate,dailyMileageKm:0,totalMileageKm:100,status:'NORMAL',dataQuality:'CARRIED_FORWARD',sourceProtocol:'MQTT'},
|
||||
]),snapshotId:'detail',nextCursor:null});
|
||||
return Response.json({code:'SUCCESS',data:[]});
|
||||
});
|
||||
t.mock.method(pool,'execute',async(sql:string)=>{
|
||||
if(!sql.includes('FROM vehicle_info'))return [[],[]];
|
||||
return [[0,1,2,3].map(i=>({plate:`TEST${i}`,vin:'VIN',customer:'客户',department:i===1?'B':'A',manager:null,
|
||||
manager_id:i===0&&reassigned?'new-owner':`${i}`,rent_status:'租赁',entity:null,project:null,inventory_location:null,brand_label:null})),[]];
|
||||
});
|
||||
let user:AuthUser={userId:'0',userName:'测试',loginName:'test',depCode:'',depName:'A',roles:[],permissionLevel:'full'};
|
||||
const app=new Hono<{Variables:{user:AuthUser}}>();
|
||||
app.use('*',async(c,next)=>{c.set('user',user);await next();});
|
||||
app.route('/monitoring',monitoring);app.route('/vehicle',recent);
|
||||
const base='/monitoring?startDate=2026-10-10&endDate=2026-10-11&sourcePriority=instrument';
|
||||
const read=async(extra='')=>{const response=await app.request(base+extra);assert.equal(response.status,200);return await response.json() as MonitoringResponse;};
|
||||
const first=await read('&limit=1');
|
||||
assert.equal(first.vehicles[0].plate,'TEST1');assert.equal(first.stats.totalToday,50);
|
||||
assert.equal(first.rangeDailyTotals?.[1].totalKm,null);
|
||||
assert.equal(requests.filter(r=>r.url.endsWith('/summary/query')).length,1);
|
||||
assert.equal(requests.filter(r=>r.url.endsWith('/range/query')).length,0);
|
||||
const second=await read('&page=2&limit=1&includeDailyTrend=0');assert.equal(second.vehicles[0].periodMileageKm,20);
|
||||
const sorted=await read('&sortOrder=asc&includeDailyTrend=0');assert.equal(sorted.vehicles[0].periodMileageKm,0);
|
||||
const exported=await read('&limit=9999&includeDailyTrend=0');
|
||||
assert.equal(exported.vehicles.length,4);assert.equal(exported.vehicles[3].periodMileageKm,null);
|
||||
assert.equal(exported.queriedAt,first.queriedAt);assert.ok(!exported.vehicles[0].dailyMileage);
|
||||
assert.equal(requests.filter(r=>r.url.endsWith('/summary/query')).length,1);
|
||||
user={...user,permissionLevel:'department'};
|
||||
const department=await read();assert.equal(department.stats.totalToday,20);
|
||||
assert.equal(department.rangeDailyTotals?.[0].totalKm,20);
|
||||
assert.deepEqual(requests.filter(r=>r.url.endsWith('/summary/query')).at(-1)!.body.plateNumbers,['TEST0','TEST2']);
|
||||
user={...user,permissionLevel:'personal'};
|
||||
const personal=await read();assert.equal(personal.rangeDailyTotals?.[0].totalKm,0);
|
||||
assert.equal(personal.rangeDailyTotals?.[1].totalKm,null);
|
||||
const count=requests.length;reassigned=true;
|
||||
const empty=await read();assert.equal(empty.total,0);assert.ok(empty.rangeDailyTotals?.every(d=>d.totalKm===null));assert.equal(requests.length,count);
|
||||
const denied=await app.request('/vehicle/TEST0/recent?start=2026-10-10&end=2026-10-11');assert.equal(denied.status,403);
|
||||
reassigned=false;user={...user,permissionLevel:'full'};
|
||||
const detail=await read('&plate=TEST2&dailyDetails=1&includeDailyTrend=0');
|
||||
assert.deepEqual(detail.vehicles[0].dailyMileage,{'2026-10-10':20,'2026-10-11':null});
|
||||
assert.deepEqual(requests.at(-1)!.body.plateNumbers,['TEST2']);
|
||||
const before=requests.filter(r=>r.url.endsWith('/summary/query')).length;
|
||||
await read('&refresh=1');assert.equal(requests.filter(r=>r.url.endsWith('/summary/query')).length,before+1);
|
||||
failure=403;const fail=await app.request(base+'&refresh=1');assert.equal(fail.status,403);assert.equal((await fail.json()).code,'UPSTREAM_ERROR');
|
||||
failure=429;const limited=await app.request(base);assert.equal(limited.status,429);assert.equal(limited.headers.get('Retry-After'),'12');
|
||||
});
|
||||
Reference in New Issue
Block a user