t18-takeover-preflight.mjs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import fs from 'node:fs/promises'
  2. import path from 'node:path'
  3. // One read-only preflight, with a new evidence directory on every run.
  4. const base = 'http://192.168.110.96:8301/api/cockpit/v1'
  5. const startedAt = new Date().toISOString()
  6. const output = path.resolve('.scratch/cockpit-real-data-wayfinding/verify/artifacts/t18',
  7. 'takeover-' + startedAt.replace(/[:.]/g, '-'))
  8. const routes = [
  9. '/foundation', '/map-snapshot?page=1&pageSize=1000',
  10. '/monitoring/overview', '/monitoring/distribution', '/monitoring/situation',
  11. '/overview/infrastructure', '/overview/monitoring', '/overview/warning-settings?range=LAST_7_DAYS',
  12. '/infrastructure/base-archives', '/infrastructure/operating-information',
  13. '/infrastructure/management-information', '/infrastructure/emergency-support',
  14. '/events/current?mode=ALARM', '/events/history-analysis?eventFamily=ALARM',
  15. '/events/handling-efficiency?eventFamily=ALARM', '/events/multidimensional-analysis?eventFamily=ALARM',
  16. '/equipments/21119740/work-orders', '/work-orders/WO202610121103507623/timeline',
  17. ]
  18. const responses = []
  19. for (const route of routes) {
  20. const requestedAt = new Date().toISOString()
  21. try {
  22. const response = await fetch(base + route, { signal: AbortSignal.timeout(10000) })
  23. const body = await response.json()
  24. responses.push({ route, requestedAt, receivedAt: new Date().toISOString(),
  25. http: response.status, body })
  26. } catch (error) {
  27. responses.push({ route, requestedAt, error: error.message })
  28. }
  29. }
  30. const checks = responses.map(row => ({
  31. id: 'endpoint-' + row.route,
  32. passed: row.http === 200 && row.body?.code === 200 && row.body?.data?.readOnly === true,
  33. evidence: { http: row.http, code: row.body?.code, readOnly: row.body?.data?.readOnly, error: row.error },
  34. }))
  35. for (const route of ['/monitoring/overview', '/monitoring/distribution', '/monitoring/situation']) {
  36. const data = responses.find(row => row.route === route)?.body?.data
  37. checks.push({ id: 'createdAt-' + route,
  38. passed: !!data && Object.hasOwn(data, 'createdAt'),
  39. evidence: { hasOwnCreatedAt: !!data && Object.hasOwn(data, 'createdAt'), value: data?.createdAt } })
  40. }
  41. const orders = responses.find(row => row.route === '/equipments/21119740/work-orders')?.body?.data?.workOrders
  42. const candidate = orders?.find(row => row.workOrderCode === 'WO202610121103507623')
  43. const report = { startedAt, endedAt: new Date().toISOString(), base, readOnly: true,
  44. page: 'http://127.0.0.1:8000/#/cockpit',
  45. versionObservations: {
  46. frontendHead: '5a83ab9b836156a4bb9468d1868e4d8040238571',
  47. frontendRemoteMaster: 'a6fbbea9dc32e83ee61dcb5a6646bf5875399636',
  48. backendHead: 'a96091ce081411e8ceedcf73fe38274d0c63fae8',
  49. backendRemoteMaster: '3e448568cd7db6c9a710479283553eab16e67817',
  50. source: 'Local git read and git ls-remote before this run; no fetch, commit or push.',
  51. runtimeCommitBinding: 'not established; frontend dev worktree has uncommitted parallel changes',
  52. },
  53. candidate, responses, checks,
  54. summary: { requests: responses.length, checks: checks.length,
  55. passed: checks.filter(row => row.passed).length, failed: checks.filter(row => !row.passed).length },
  56. scope: 'Current GET response and contract presence preflight only; no business writes, no mock, no complete human verdict, no proof of all map/panel/WS behavior.',
  57. }
  58. await fs.mkdir(output, { recursive: true })
  59. const file = path.join(output, 'preflight.json')
  60. await fs.writeFile(file, JSON.stringify(report, null, 2))
  61. console.log(JSON.stringify({ file, summary: report.summary, candidate: candidate && {
  62. workOrderCode: candidate.workOrderCode, stateCode: candidate.stateCode,
  63. associatedWithCurrentEvent: candidate.associatedWithCurrentEvent },
  64. failures: checks.filter(row => !row.passed) }))
  65. process.exitCode = report.summary.failed ? 1 : 0