Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions tests/unit/lib/cachedRoute.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,34 @@ test('cachedRoute - null key parts', async (t) => {
await cachedRoute(mockCtx, ['test', null, null], '/test', async () => ({}), false)
t.pass()
})

test('cachedRoute - overwriteCache bypasses in-flight dedup and refetches', async (t) => {
const mockCtx = {
conf: { cacheTiming: { '/test': '30s' } },
lru_30s: { get: () => undefined, set: () => {} },
queuedRequests: new Map()
}

let inflightResolved = false
const inflight = cachedRoute(mockCtx, ['k'], '/test', async () => {
await new Promise(resolve => setTimeout(resolve, 50))
inflightResolved = true
return { data: 'stale' }
}, false)

await new Promise(resolve => setTimeout(resolve, 10))
t.ok(mockCtx.queuedRequests.has('k'), 'a request is already in flight for the key')

let overwriteRan = false
const fresh = await cachedRoute(mockCtx, ['k'], '/test', async () => {
overwriteRan = true
return { data: 'fresh' }
}, true)

t.is(fresh.data, 'fresh', 'overwriteCache returns its own fresh result')
t.ok(overwriteRan, 'overwriteCache ran its own fetch instead of joining the queue')
t.absent(inflightResolved, 'overwriteCache did not block on the in-flight request')

await inflight
t.pass()
})
25 changes: 25 additions & 0 deletions tests/unit/lib/work.order.export.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,28 @@ test('work.order.export: RMA CSV maps a MicroBT Miner WO to the fixed columns',
t.is(row[9], new Date(wo.info.closedAt).toISOString().slice(0, 10), 'Repair Date from closedAt')
t.is(row[10], 'eng@test', 'Engineer')
})

test('work.order.export: RMA CSV maps a known miner type slug to its friendly model name', (t) => {
const wo = {
code: 'IVI-1-0090',
info: {
type: 1,
deviceModel: 'miner-wm-m63spp',
deviceIdentifier: 'MINER-SN-9',
createdAt: 1730000000000,
partsMoves: [{ role: 'diagnosis', partCode: 'HB-1' }]
}
}
const row = renderRmaCsv([wo]).trim().split('\r\n')[1].split(',')
t.is(row[1], 'M63S', 'Repaired type shows the friendly model name, not the worker slug')
t.is(row[8], 'M63S', 'Miner Model column shows the friendly name too')
})

test('work.order.export: RMA CSV leaves an unmapped deviceModel untouched', (t) => {
const wo = {
code: 'IVI-1-0091',
info: { type: 1, deviceModel: 'miner-am-s21', deviceIdentifier: 'SN', partsMoves: [] }
}
const row = renderRmaCsv([wo]).trim().split('\r\n')[1].split(',')
t.is(row[1], 'miner-am-s21', 'unmapped model falls back to the raw value')
})
5 changes: 5 additions & 0 deletions workers/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ const RMA_COLUMNS = [
'Engineer'
]

const MINER_MODEL_DISPLAY_NAMES = {
'miner-wm-m63spp': 'M63S'
}

const HTTP_METHODS = {
GET: 'GET',
POST: 'POST',
Expand Down Expand Up @@ -875,5 +879,6 @@ module.exports = {
WORK_ORDER_FILE_MIME_ALLOWLIST_DEFAULT,
WORK_ORDER_EXPORT_FORMATS,
RMA_COLUMNS,
MINER_MODEL_DISPLAY_NAMES,
MICROSOFT_AUTH_SCOPE
}
10 changes: 7 additions & 3 deletions workers/lib/server/lib/cachedRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ async function cachedRoute (ctx, ckeyParts, apiPath, func, overwriteCache = fals

const ckey = ckeyParts.map(k => k ?? '-').join(':')

if (!overwriteCache) {
const cached = lru.get(ckey)
if (cached !== undefined) return cached
if (overwriteCache) {
const data = await func()
lru.set(ckey, data)
return data
}

const cached = lru.get(ckey)
if (cached !== undefined) return cached

const requests = ctx.queuedRequests

if (requests.has(ckey)) {
Expand Down
12 changes: 9 additions & 3 deletions workers/lib/server/lib/work.order.export.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
'use strict'

const { csvEscape } = require('../../utils')
const { RMA_COLUMNS } = require('../../constants')
const { RMA_COLUMNS, MINER_MODEL_DISPLAY_NAMES } = require('../../constants')

function displayMinerModel (model) {
if (!model) return model
return MINER_MODEL_DISPLAY_NAMES[String(model).toLowerCase()] || model
}

function renderWorkOrderCsv (wo) {
const { partsMoves, ...woFields } = wo.info || {}
Expand Down Expand Up @@ -33,16 +38,17 @@ function renderRmaCsv (workOrders) {
const repaired = moves.find(m => m.role === 'repaired') || moves.find(m => m.role === 'diagnosis') || moves[0] || {}
const replaced = moves.find(m => m.role === 'replacement') || repaired
const repairTs = info.closedAt ?? info.createdAt
const minerModel = displayMinerModel(info.deviceModel)
return [
wo.code,
info.deviceModel,
minerModel,
info.deviceIdentifier,
repaired.partCode,
replaced.partCode,
info.issue,
info.finalResult,
info.remarks,
info.deviceModel,
minerModel,
repairTs ? new Date(repairTs).toISOString().slice(0, 10) : '',
info.assignedTo ?? info.createdBy
]
Expand Down
Loading