✓ findAdArchiveIdOccurrences finds a single id
✓ extractBalancedBlockAround returns the whole simple object
✓ handles nested objects correctly
✓ ignores braces inside an escaped string (literal { in ad copy)
✓ handles escaped quotes inside a string value
✓ splitIntoAdBlocks separates two concatenated ad objects
✓ splitIntoAdBlocks dedupes repeated ad_archive_id in the same text
✓ a truncated (never-closing) block returns null instead of throwing
✓ splitIntoAdBlocks skips truncated ads and keeps well-formed ones
✓ returns an empty array when there is no ad_archive_id at all
✓ an odd quote in preceding text breaks extraction of the payload after it
✓ the same payload extracts fine when it is not concatenated with that text
✓ extracts a full set of fields from a synthetic block
✓ missing fields come back as null, not throwing
✓ unescapes JSON string content (accents, quotes, unicode)
✓ body.text pattern does not leak a sibling field's title
✓ disabled filter always matches
✓ DEFAULT_FILTERS is enabled by default and matches an ad with no extra criteria set
✓ minCollationCount: below the threshold is excluded
✓ minCollationCount: exactly at the threshold matches
✓ minCollationCount: above the threshold matches
✓ missing collationCount is treated as 0
✓ daysInAir mode 'more' excludes ads younger than the threshold
✓ daysInAir mode 'more' includes ads older than the threshold
✓ daysInAir mode 'less' excludes ads older than the threshold
✓ removeHidden excludes ads flagged as hidden/house ads
✓ countMatches counts only the ads that pass
✓ minCollationCount null does not hide ads that have no collationCount
✓ a real threshold still excludes ads with no collationCount
✓ login returns the token/user/expiry on success
✓ login surfaces the server error message on failure
✓ login falls back to a generic error when the server gives none
✓ fetchSessionStatus sends the bearer token and returns the body
✓ fetchSessionStatus treats 401 as a real logout signal
✓ fetchSessionStatus treats a 500 as transient, not a logout
✓ favoriteAd sends the bearer token and the ad payload, returns favorite state
✓ favoriteAd surfaces the server error on failure
✓ unlockWhatsapp sends the bearer token and the ad payload, returns the found result
✓ unlockWhatsapp surfaces the server error on failure
✓ getApiBaseUrl falls back to the production default
✓ setApiBaseUrl then getApiBaseUrl returns the override
✓ setApiBaseUrl strips a trailing slash
✓ setApiBaseUrl with blank input resets to the default
✓ conta os dias desde o inicio da veiculacao
✓ comecou hoje = 0 dias
✓ desvio pequeno de fuso/relogio vira 0, nao negativo
✓ futuro demais e dado ruim, nao 'hoje'
✓ sem data nao inventa numero
✓ epoch em MILISSEGUNDOS e recusado em vez de virar 20 mil dias
✓ rotulo tem singular, plural e o caso de hoje
✓ rotulo sem dado devolve null (o badge omite a linha)
✓ caso real: anuncio iniciado em 25/06/2026 visto em 31/08/2026
✓ le a data do texto do card em pt-BR
✓ le a data do texto do card em en-US
✓ todos os meses em pt-BR sao reconhecidos
✓ texto sem data de inicio devolve null
✓ card inteiro: acha a data no meio de todo o texto
✓ remove APP1/Exif
✓ remove APP2/ICC_PROFILE, que e o que o canvas reintroduz
✓ remove APP0/JFIF e comentario
✓ dados comprimidos depois do SOS ficam intactos
✓ nao mexe no que nao e JPEG
✓ JPEG sem nenhum APP sai identico
✓ tamanho invalido nao entra em laco infinito nem estoura
✓ udta vira free e o conteudo e zerado
✓ tamanho do arquivo NAO muda (offsets do stco dependem disso)
✓ mdat fica intacto
✓ uuid (XMP) tambem some
✓ meta aninhado dentro de trak tambem e alcancado
✓ mvhd tem as datas zeradas mas o resto preservado
✓ box com tamanho invalido nao trava nem corrompe
✓ arquivo sem metadado nenhum sai identico
✓ telefone do contato da Pagina: caso real Bruna Confeiteira
✓ caso real Dona Lucia: contato com 9 digitos passa intacto
✓ celular ja com nono digito passa intacto
✓ formato internacional +55
✓ fixo nao vira WhatsApp
✓ DDD inexistente e recusado
✓ numero solto sem formatacao nao vira telefone
✓ link do CTA de post organico: caso real Dona Lucia
✓ link tem prioridade sobre telefone do cadastro
✓ wa.me e whatsapp:// tambem casam
✓ api.whatsapp.com generico, sem numero, nao acha nada
✓ normalizarTexto tira emoji, acento e pontuacao
✓ mesmo criativo com variacoes de copy ainda casa
✓ oferta diferente do mesmo nicho NAO casa
✓ escolhe o post da oferta certa entre varias da mesma Pagina
✓ sem post parecido, nao chuta
✓ numero unico na Pagina serve mesmo sem casar texto
✓ numeros diferentes sem casamento = null (nao escolhe no chute)
✓ casamento tem prioridade sobre numero unico
✓ anuncio sem texto ainda aproveita o numero unico da Pagina
✓ entradas vazias nao quebram
✓ post sem numero e ignorado
✓ slug numerico (Pagina com id novo)
✓ slug de username (Pagina antiga)
✓ barras escapadas do JSON do Facebook
✓ profile.php nao tem slug utilizavel
✓ entrada vazia ou estranha nao quebra
✓ drain returns everything pushed
✓ clear empties the buffer
✓ caps total size, discarding the oldest content
✓ drain empties the buffer, so the same text is never rescanned
✓ ignores empty/falsy pushes without throwing
✓ getSession returns null when nothing was saved
✓ saveSession then getSession round-trips the data
✓ getSession returns null once expiresAt is in the past
✓ getSession returns null inside the safety margin before expiry
✓ clearSession removes the stored session
✓ getSession returns null for a malformed stored value (no crash)
> test:extension
> node --test extension/src/lib/*.test.mjs
TAP version 13
# Subtest: findAdArchiveIdOccurrences finds a single id
ok 1 - findAdArchiveIdOccurrences finds a single id
---
duration_ms: 1.36241
type: 'test'
...
# Subtest: extractBalancedBlockAround returns the whole simple object
ok 2 - extractBalancedBlockAround returns the whole simple object
---
duration_ms: 2.317043
type: 'test'
...
# Subtest: handles nested objects correctly
ok 3 - handles nested objects correctly
---
duration_ms: 0.166221
type: 'test'
...
# Subtest: ignores braces inside an escaped string (literal { in ad copy)
ok 4 - ignores braces inside an escaped string (literal { in ad copy)
---
duration_ms: 0.251121
type: 'test'
...
# Subtest: handles escaped quotes inside a string value
ok 5 - handles escaped quotes inside a string value
---
duration_ms: 0.217831
type: 'test'
...
# Subtest: splitIntoAdBlocks separates two concatenated ad objects
ok 6 - splitIntoAdBlocks separates two concatenated ad objects
---
duration_ms: 0.943631
type: 'test'
...
# Subtest: splitIntoAdBlocks dedupes repeated ad_archive_id in the same text
ok 7 - splitIntoAdBlocks dedupes repeated ad_archive_id in the same text
---
duration_ms: 0.24225
type: 'test'
...
# Subtest: a truncated (never-closing) block returns null instead of throwing
ok 8 - a truncated (never-closing) block returns null instead of throwing
---
duration_ms: 0.228099
type: 'test'
...
# Subtest: splitIntoAdBlocks skips truncated ads and keeps well-formed ones
ok 9 - splitIntoAdBlocks skips truncated ads and keeps well-formed ones
---
duration_ms: 0.47688
type: 'test'
...
# Subtest: returns an empty array when there is no ad_archive_id at all
ok 10 - returns an empty array when there is no ad_archive_id at all
---
duration_ms: 0.37637
type: 'test'
...
# Subtest: an odd quote in preceding text breaks extraction of the payload after it
ok 11 - an odd quote in preceding text breaks extraction of the payload after it
---
duration_ms: 0.22817
type: 'test'
...
# Subtest: the same payload extracts fine when it is not concatenated with that text
ok 12 - the same payload extracts fine when it is not concatenated with that text
---
duration_ms: 0.118431
type: 'test'
...
# Subtest: extracts a full set of fields from a synthetic block
ok 13 - extracts a full set of fields from a synthetic block
---
duration_ms: 1.739702
type: 'test'
...
# Subtest: missing fields come back as null, not throwing
ok 14 - missing fields come back as null, not throwing
---
duration_ms: 0.813211
type: 'test'
...
# Subtest: unescapes JSON string content (accents, quotes, unicode)
ok 15 - unescapes JSON string content (accents, quotes, unicode)
---
duration_ms: 0.49041
type: 'test'
...
# Subtest: body.text pattern does not leak a sibling field's title
ok 16 - body.text pattern does not leak a sibling field's title
---
duration_ms: 1.762912
type: 'test'
...
# Subtest: disabled filter always matches
ok 17 - disabled filter always matches
---
duration_ms: 1.226141
type: 'test'
...
# Subtest: DEFAULT_FILTERS is enabled by default and matches an ad with no extra criteria set
ok 18 - DEFAULT_FILTERS is enabled by default and matches an ad with no extra criteria set
---
duration_ms: 2.137072
type: 'test'
...
# Subtest: minCollationCount: below the threshold is excluded
ok 19 - minCollationCount: below the threshold is excluded
---
duration_ms: 0.23604
type: 'test'
...
# Subtest: minCollationCount: exactly at the threshold matches
ok 20 - minCollationCount: exactly at the threshold matches
---
duration_ms: 0.17206
type: 'test'
...
# Subtest: minCollationCount: above the threshold matches
ok 21 - minCollationCount: above the threshold matches
---
duration_ms: 0.14447
type: 'test'
...
# Subtest: missing collationCount is treated as 0
ok 22 - missing collationCount is treated as 0
---
duration_ms: 0.13651
type: 'test'
...
# Subtest: daysInAir mode 'more' excludes ads younger than the threshold
ok 23 - daysInAir mode 'more' excludes ads younger than the threshold
---
duration_ms: 0.231169
type: 'test'
...
# Subtest: daysInAir mode 'more' includes ads older than the threshold
ok 24 - daysInAir mode 'more' includes ads older than the threshold
---
duration_ms: 0.156231
type: 'test'
...
# Subtest: daysInAir mode 'less' excludes ads older than the threshold
ok 25 - daysInAir mode 'less' excludes ads older than the threshold
---
duration_ms: 0.53294
type: 'test'
...
# Subtest: removeHidden excludes ads flagged as hidden/house ads
ok 26 - removeHidden excludes ads flagged as hidden/house ads
---
duration_ms: 0.47248
type: 'test'
...
# Subtest: countMatches counts only the ads that pass
ok 27 - countMatches counts only the ads that pass
---
duration_ms: 0.250379
type: 'test'
...
# Subtest: minCollationCount null does not hide ads that have no collationCount
ok 28 - minCollationCount null does not hide ads that have no collationCount
---
duration_ms: 0.137329
type: 'test'
...
# Subtest: a real threshold still excludes ads with no collationCount
ok 29 - a real threshold still excludes ads with no collationCount
---
duration_ms: 0.269881
type: 'test'
...
# Subtest: login returns the token/user/expiry on success
ok 30 - login returns the token/user/expiry on success
---
duration_ms: 4.629394
type: 'test'
...
# Subtest: login surfaces the server error message on failure
ok 31 - login surfaces the server error message on failure
---
duration_ms: 1.18657
type: 'test'
...
# Subtest: login falls back to a generic error when the server gives none
ok 32 - login falls back to a generic error when the server gives none
---
duration_ms: 1.535871
type: 'test'
...
# Subtest: fetchSessionStatus sends the bearer token and returns the body
ok 33 - fetchSessionStatus sends the bearer token and returns the body
---
duration_ms: 1.465272
type: 'test'
...
# Subtest: fetchSessionStatus treats 401 as a real logout signal
ok 34 - fetchSessionStatus treats 401 as a real logout signal
---
duration_ms: 1.314411
type: 'test'
...
# Subtest: fetchSessionStatus treats a 500 as transient, not a logout
ok 35 - fetchSessionStatus treats a 500 as transient, not a logout
---
duration_ms: 3.205863
type: 'test'
...
# Subtest: favoriteAd sends the bearer token and the ad payload, returns favorite state
ok 36 - favoriteAd sends the bearer token and the ad payload, returns favorite state
---
duration_ms: 2.179482
type: 'test'
...
# Subtest: favoriteAd surfaces the server error on failure
ok 37 - favoriteAd surfaces the server error on failure
---
duration_ms: 1.185071
type: 'test'
...
# Subtest: unlockWhatsapp sends the bearer token and the ad payload, returns the found result
ok 38 - unlockWhatsapp sends the bearer token and the ad payload, returns the found result
---
duration_ms: 1.535732
type: 'test'
...
# Subtest: unlockWhatsapp surfaces the server error on failure
ok 39 - unlockWhatsapp surfaces the server error on failure
---
duration_ms: 1.531402
type: 'test'
...
# Subtest: getApiBaseUrl falls back to the production default
ok 40 - getApiBaseUrl falls back to the production default
---
duration_ms: 3.192342
type: 'test'
...
# Subtest: setApiBaseUrl then getApiBaseUrl returns the override
ok 41 - setApiBaseUrl then getApiBaseUrl returns the override
---
duration_ms: 5.190083
type: 'test'
...
# Subtest: setApiBaseUrl strips a trailing slash
ok 42 - setApiBaseUrl strips a trailing slash
---
duration_ms: 4.578594
type: 'test'
...
# Subtest: setApiBaseUrl with blank input resets to the default
ok 43 - setApiBaseUrl with blank input resets to the default
---
duration_ms: 0.756471
type: 'test'
...
# Subtest: conta os dias desde o inicio da veiculacao
ok 44 - conta os dias desde o inicio da veiculacao
---
duration_ms: 2.761772
type: 'test'
...
# Subtest: comecou hoje = 0 dias
ok 45 - comecou hoje = 0 dias
---
duration_ms: 0.304391
type: 'test'
...
# Subtest: desvio pequeno de fuso/relogio vira 0, nao negativo
ok 46 - desvio pequeno de fuso/relogio vira 0, nao negativo
---
duration_ms: 0.18077
type: 'test'
...
# Subtest: futuro demais e dado ruim, nao 'hoje'
ok 47 - futuro demais e dado ruim, nao 'hoje'
---
duration_ms: 0.114251
type: 'test'
...
# Subtest: sem data nao inventa numero
ok 48 - sem data nao inventa numero
---
duration_ms: 1.366771
type: 'test'
...
# Subtest: epoch em MILISSEGUNDOS e recusado em vez de virar 20 mil dias
ok 49 - epoch em MILISSEGUNDOS e recusado em vez de virar 20 mil dias
---
duration_ms: 0.620232
type: 'test'
...
# Subtest: rotulo tem singular, plural e o caso de hoje
ok 50 - rotulo tem singular, plural e o caso de hoje
---
duration_ms: 0.26601
type: 'test'
...
# Subtest: rotulo sem dado devolve null (o badge omite a linha)
ok 51 - rotulo sem dado devolve null (o badge omite a linha)
---
duration_ms: 0.12379
type: 'test'
...
# Subtest: caso real: anuncio iniciado em 25/06/2026 visto em 31/08/2026
ok 52 - caso real: anuncio iniciado em 25/06/2026 visto em 31/08/2026
---
duration_ms: 0.41539
type: 'test'
...
# Subtest: le a data do texto do card em pt-BR
ok 53 - le a data do texto do card em pt-BR
---
duration_ms: 0.919201
type: 'test'
...
# Subtest: le a data do texto do card em en-US
ok 54 - le a data do texto do card em en-US
---
duration_ms: 0.416052
type: 'test'
...
# Subtest: todos os meses em pt-BR sao reconhecidos
ok 55 - todos os meses em pt-BR sao reconhecidos
---
duration_ms: 1.819042
type: 'test'
...
# Subtest: texto sem data de inicio devolve null
ok 56 - texto sem data de inicio devolve null
---
duration_ms: 0.31135
type: 'test'
...
# Subtest: card inteiro: acha a data no meio de todo o texto
ok 57 - card inteiro: acha a data no meio de todo o texto
---
duration_ms: 0.147251
type: 'test'
...
# Subtest: remove APP1/Exif
ok 58 - remove APP1/Exif
---
duration_ms: 2.867553
type: 'test'
...
# Subtest: remove APP2/ICC_PROFILE, que e o que o canvas reintroduz
ok 59 - remove APP2/ICC_PROFILE, que e o que o canvas reintroduz
---
duration_ms: 0.265161
type: 'test'
...
# Subtest: remove APP0/JFIF e comentario
ok 60 - remove APP0/JFIF e comentario
---
duration_ms: 0.580222
type: 'test'
...
# Subtest: dados comprimidos depois do SOS ficam intactos
ok 61 - dados comprimidos depois do SOS ficam intactos
---
duration_ms: 1.007141
type: 'test'
...
# Subtest: nao mexe no que nao e JPEG
ok 62 - nao mexe no que nao e JPEG
---
duration_ms: 0.211111
type: 'test'
...
# Subtest: JPEG sem nenhum APP sai identico
ok 63 - JPEG sem nenhum APP sai identico
---
duration_ms: 0.22628
type: 'test'
...
# Subtest: tamanho invalido nao entra em laco infinito nem estoura
ok 64 - tamanho invalido nao entra em laco infinito nem estoura
---
duration_ms: 0.25665
type: 'test'
...
# Subtest: udta vira free e o conteudo e zerado
ok 65 - udta vira free e o conteudo e zerado
---
duration_ms: 5.764874
type: 'test'
...
# Subtest: tamanho do arquivo NAO muda (offsets do stco dependem disso)
ok 66 - tamanho do arquivo NAO muda (offsets do stco dependem disso)
---
duration_ms: 0.46788
type: 'test'
...
# Subtest: mdat fica intacto
ok 67 - mdat fica intacto
---
duration_ms: 0.537779
type: 'test'
...
# Subtest: uuid (XMP) tambem some
ok 68 - uuid (XMP) tambem some
---
duration_ms: 0.366141
type: 'test'
...
# Subtest: meta aninhado dentro de trak tambem e alcancado
ok 69 - meta aninhado dentro de trak tambem e alcancado
---
duration_ms: 0.33434
type: 'test'
...
# Subtest: mvhd tem as datas zeradas mas o resto preservado
ok 70 - mvhd tem as datas zeradas mas o resto preservado
---
duration_ms: 0.287459
type: 'test'
...
# Subtest: box com tamanho invalido nao trava nem corrompe
ok 71 - box com tamanho invalido nao trava nem corrompe
---
duration_ms: 0.34504
type: 'test'
...
# Subtest: arquivo sem metadado nenhum sai identico
ok 72 - arquivo sem metadado nenhum sai identico
---
duration_ms: 1.010821
type: 'test'
...
# Subtest: telefone do contato da Pagina: caso real Bruna Confeiteira
ok 73 - telefone do contato da Pagina: caso real Bruna Confeiteira
---
duration_ms: 1.693502
type: 'test'
...
# Subtest: caso real Dona Lucia: contato com 9 digitos passa intacto
ok 74 - caso real Dona Lucia: contato com 9 digitos passa intacto
---
duration_ms: 0.44932
type: 'test'
...
# Subtest: celular ja com nono digito passa intacto
ok 75 - celular ja com nono digito passa intacto
---
duration_ms: 1.779012
type: 'test'
...
# Subtest: formato internacional +55
ok 76 - formato internacional +55
---
duration_ms: 0.19855
type: 'test'
...
# Subtest: fixo nao vira WhatsApp
ok 77 - fixo nao vira WhatsApp
---
duration_ms: 0.235161
type: 'test'
...
# Subtest: DDD inexistente e recusado
ok 78 - DDD inexistente e recusado
---
duration_ms: 0.143269
type: 'test'
...
# Subtest: numero solto sem formatacao nao vira telefone
ok 79 - numero solto sem formatacao nao vira telefone
---
duration_ms: 0.31173
type: 'test'
...
# Subtest: link do CTA de post organico: caso real Dona Lucia
ok 80 - link do CTA de post organico: caso real Dona Lucia
---
duration_ms: 0.474841
type: 'test'
...
# Subtest: link tem prioridade sobre telefone do cadastro
ok 81 - link tem prioridade sobre telefone do cadastro
---
duration_ms: 0.44205
type: 'test'
...
# Subtest: wa.me e whatsapp:// tambem casam
ok 82 - wa.me e whatsapp:// tambem casam
---
duration_ms: 0.525521
type: 'test'
...
# Subtest: api.whatsapp.com generico, sem numero, nao acha nada
ok 83 - api.whatsapp.com generico, sem numero, nao acha nada
---
duration_ms: 0.314981
type: 'test'
...
# Subtest: normalizarTexto tira emoji, acento e pontuacao
ok 84 - normalizarTexto tira emoji, acento e pontuacao
---
duration_ms: 4.834575
type: 'test'
...
# Subtest: mesmo criativo com variacoes de copy ainda casa
ok 85 - mesmo criativo com variacoes de copy ainda casa
---
duration_ms: 0.77773
type: 'test'
...
# Subtest: oferta diferente do mesmo nicho NAO casa
ok 86 - oferta diferente do mesmo nicho NAO casa
---
duration_ms: 0.63866
type: 'test'
...
# Subtest: escolhe o post da oferta certa entre varias da mesma Pagina
ok 87 - escolhe o post da oferta certa entre varias da mesma Pagina
---
duration_ms: 0.744201
type: 'test'
...
# Subtest: sem post parecido, nao chuta
ok 88 - sem post parecido, nao chuta
---
duration_ms: 0.39453
type: 'test'
...
# Subtest: numero unico na Pagina serve mesmo sem casar texto
ok 89 - numero unico na Pagina serve mesmo sem casar texto
---
duration_ms: 0.280811
type: 'test'
...
# Subtest: numeros diferentes sem casamento = null (nao escolhe no chute)
ok 90 - numeros diferentes sem casamento = null (nao escolhe no chute)
---
duration_ms: 0.19603
type: 'test'
...
# Subtest: casamento tem prioridade sobre numero unico
ok 91 - casamento tem prioridade sobre numero unico
---
duration_ms: 1.973772
type: 'test'
...
# Subtest: anuncio sem texto ainda aproveita o numero unico da Pagina
ok 92 - anuncio sem texto ainda aproveita o numero unico da Pagina
---
duration_ms: 0.296669
type: 'test'
...
# Subtest: entradas vazias nao quebram
ok 93 - entradas vazias nao quebram
---
duration_ms: 0.48784
type: 'test'
...
# Subtest: post sem numero e ignorado
ok 94 - post sem numero e ignorado
---
duration_ms: 0.285571
type: 'test'
...
# Subtest: slug numerico (Pagina com id novo)
ok 95 - slug numerico (Pagina com id novo)
---
duration_ms: 0.24203
type: 'test'
...
# Subtest: slug de username (Pagina antiga)
ok 96 - slug de username (Pagina antiga)
---
duration_ms: 0.154629
type: 'test'
...
# Subtest: barras escapadas do JSON do Facebook
ok 97 - barras escapadas do JSON do Facebook
---
duration_ms: 0.15454
type: 'test'
...
# Subtest: profile.php nao tem slug utilizavel
ok 98 - profile.php nao tem slug utilizavel
---
duration_ms: 0.112551
type: 'test'
...
# Subtest: entrada vazia ou estranha nao quebra
ok 99 - entrada vazia ou estranha nao quebra
---
duration_ms: 0.095661
type: 'test'
...
# Subtest: drain returns everything pushed
ok 100 - drain returns everything pushed
---
duration_ms: 12.26841
type: 'test'
...
# Subtest: clear empties the buffer
ok 101 - clear empties the buffer
---
duration_ms: 0.25678
type: 'test'
...
# Subtest: caps total size, discarding the oldest content
ok 102 - caps total size, discarding the oldest content
---
duration_ms: 0.21198
type: 'test'
...
# Subtest: drain empties the buffer, so the same text is never rescanned
ok 103 - drain empties the buffer, so the same text is never rescanned
---
duration_ms: 0.15986
type: 'test'
...
# Subtest: ignores empty/falsy pushes without throwing
ok 104 - ignores empty/falsy pushes without throwing
---
duration_ms: 2.354141
type: 'test'
...
# Subtest: getSession returns null when nothing was saved
ok 105 - getSession returns null when nothing was saved
---
duration_ms: 2.374723
type: 'test'
...
# Subtest: saveSession then getSession round-trips the data
ok 106 - saveSession then getSession round-trips the data
---
duration_ms: 3.025533
type: 'test'
...
# Subtest: getSession returns null once expiresAt is in the past
ok 107 - getSession returns null once expiresAt is in the past
---
duration_ms: 2.027482
type: 'test'
...
# Subtest: getSession returns null inside the safety margin before expiry
ok 108 - getSession returns null inside the safety margin before expiry
---
duration_ms: 0.76768
type: 'test'
...
# Subtest: clearSession removes the stored session
ok 109 - clearSession removes the stored session
---
duration_ms: 1.180501
type: 'test'
...
# Subtest: getSession returns null for a malformed stored value (no crash)
ok 110 - getSession returns null for a malformed stored value (no crash)
---
duration_ms: 0.66404
type: 'test'
...
1..110
# tests 110
# suites 0
# pass 110
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 1478.061413