OverviewFilesCommitsIssuesDocsReleasesPeers

favela-surface-android / test / safe-json.test.js

47 lines · javascript ref:
import test from 'brittle'
import { safeStringify } from '../lib/host/safe-json.js'

test('ordinary payloads pass through untouched', (t) => {
  const r = safeStringify({ t: 'log', stage: 'node ready', n: 1, ok: true, nil: null })
  t.absent(r.degraded)
  t.alike(JSON.parse(r.line), { t: 'log', stage: 'node ready', n: 1, ok: true, nil: null })
  t.is(r.dropped.length, 0)
})

test('★★★ a circular payload still produces a line, and NAMES what closed the circle', (t) => {
  // The handset's shape: a Bare timer reachable from a log payload.
  function Scheduler () { this._timeouts = { _data: [] } }
  function Timeout (s) { this._scheduler = s }
  const sched = new Scheduler()
  sched._timeouts._data.push(new Timeout(sched))

  const r = safeStringify({ t: 'log', msg: 'ota: something', detail: sched })
  t.ok(r.degraded, 'it had to degrade')
  const parsed = JSON.parse(r.line)
  t.is(parsed.msg, 'ota: something', 'the part that was serializable survived')
  t.ok(r.dropped.some((d) => /circular/.test(d)), 'and the circle is reported rather than guessed at: ' + r.dropped.join(', '))
})

test('⛔ it never throws — that is the entire point', (t) => {
  const evil = {}
  Object.defineProperty(evil, 'boom', { enumerable: true, get () { throw new Error('nope') } })
  const r = safeStringify({ t: 'log', evil })
  t.ok(typeof r.line === 'string' && r.line.length > 0, 'a line came back')
  t.ok(r.degraded)
  JSON.parse(r.line) // and it is valid JSON
})

test('functions and bigints are dropped by name, not silently', (t) => {
  const r = safeStringify({ t: 'log', fn () {}, big: 10n, keep: 'yes' })
  t.is(JSON.parse(r.line).keep, 'yes')
  t.ok(r.dropped.some((d) => /fn:function/.test(d)))
  t.ok(r.dropped.some((d) => /big:bigint/.test(d)))
})

test('a root that JSON cannot represent still yields valid JSON', (t) => {
  const r = safeStringify(undefined)
  const parsed = JSON.parse(r.line)
  t.ok(r.degraded)
  t.ok(/not serializable/.test(parsed.msg))
})