Snapshot: alpha-2026-9
This commit is contained in:
parent
9acf5a97e2
commit
5accd5f3eb
242 changed files with 85549 additions and 2409 deletions
144
frontend/src/tests/short-id.js
Normal file
144
frontend/src/tests/short-id.js
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import {
|
||||
encodeShortId, decodeShortId, deserializeShortId, serializeShortId,
|
||||
encodeDomainQualifiedShortId, decodeDomainQualifiedShortId, isDomainQualifiedShortId
|
||||
} from '../short-id.js'
|
||||
|
||||
test('encodes the worked example from docs/handles-and-shortids.md', () => {
|
||||
const token = encodeShortId([0, 7, 42])
|
||||
expect(token).toBe('~DyU')
|
||||
})
|
||||
|
||||
test('kind ids are scoped per arity: id 0 means item (arity 2) or category (arity 1)', () => {
|
||||
expect(deserializeShortId(decodeShortId(encodeShortId([0, 7, 42])))).toEqual({
|
||||
kind: 'item', owner_identity_id: 7, item_local_id: 42
|
||||
})
|
||||
expect(deserializeShortId(decodeShortId(encodeShortId([0, 5])))).toEqual({
|
||||
kind: 'category', category_id: 5
|
||||
})
|
||||
})
|
||||
|
||||
test('every kind round-trips through serialize/encode/decode/deserialize', () => {
|
||||
const cases = [
|
||||
{kind: 'item', owner_identity_id: 7, item_local_id: 42},
|
||||
{kind: 'category', category_id: 5},
|
||||
{kind: 'group_item', owner_group_id: 5, item_local_id: 42},
|
||||
{kind: 'group', group_id: 11},
|
||||
{kind: 'storage_location', owner_identity_id: 3, storage_location_id: 1000},
|
||||
{kind: 'group_storage_location', owner_group_id: 5, storage_location_id: 1000},
|
||||
{kind: 'file', file_id: 123},
|
||||
{kind: 'workflow', owner_identity_id: 2, workflow_id: 9},
|
||||
]
|
||||
for (const named of cases) {
|
||||
const token = encodeShortId(serializeShortId(named))
|
||||
expect(deserializeShortId(decodeShortId(token))).toEqual(named)
|
||||
}
|
||||
})
|
||||
|
||||
test('a non-last field may be 0 - only the last field is restricted', () => {
|
||||
const named = {kind: 'item', owner_identity_id: 0, item_local_id: 42}
|
||||
const token = encodeShortId(serializeShortId(named))
|
||||
expect(deserializeShortId(decodeShortId(token))).toEqual(named)
|
||||
})
|
||||
|
||||
test('rejects a 0 last-field value, for a single-field kind', () => {
|
||||
expect(() => encodeShortId(serializeShortId({kind: 'category', category_id: 0}))).toThrow()
|
||||
})
|
||||
|
||||
test('rejects a 0 last-field value, for a multi-field kind', () => {
|
||||
expect(() => encodeShortId(serializeShortId({kind: 'item', owner_identity_id: 7, item_local_id: 0}))).toThrow()
|
||||
})
|
||||
|
||||
test('decoding never over-reads: a single-chunk field that exactly exhausts padding is not ' +
|
||||
'mistaken for a second field', () => {
|
||||
// category_id 5 -> 7 bits + 5 zero padding bits, identical to a genuine one-chunk zero field - the case the last-field-nonzero rule disambiguates.
|
||||
const token = encodeShortId([0, 5])
|
||||
expect(token).toBe('~Cg')
|
||||
expect(decodeShortId(token)).toEqual([0, 5])
|
||||
})
|
||||
|
||||
test('round-trips small and large nonzero values, in both last and non-last field position', () => {
|
||||
const values = [1, 7, 42, 100, 10000, 100000, 123456789]
|
||||
for (const value of values) {
|
||||
expect(decodeShortId(encodeShortId([0, value]))).toEqual([0, value])
|
||||
expect(decodeShortId(encodeShortId([0, value, 1]))).toEqual([0, value, 1])
|
||||
}
|
||||
})
|
||||
|
||||
test('tokens are URL-safe: only unreserved characters, plus the leading ~', () => {
|
||||
const token = encodeShortId([0, 123456, 987654])
|
||||
expect(token[0]).toBe('~')
|
||||
expect(token.slice(1)).toMatch(/^[A-Za-z0-9\-_]+$/)
|
||||
})
|
||||
|
||||
test('kinds at escaped ids (3+) round-trip through the escape tag, not just the direct 0-2 range', () => {
|
||||
const token = encodeShortId(serializeShortId({kind: 'workflow', owner_identity_id: 2, workflow_id: 9}))
|
||||
expect(deserializeShortId(decodeShortId(token))).toEqual({
|
||||
kind: 'workflow', owner_identity_id: 2, workflow_id: 9
|
||||
})
|
||||
})
|
||||
|
||||
test('serializeShortId then deserializeShortId round-trips through encode/decode', () => {
|
||||
const token = encodeShortId(serializeShortId({kind: 'item', owner_identity_id: 7, item_local_id: 42}))
|
||||
expect(token).toBe('~DyU')
|
||||
expect(deserializeShortId(decodeShortId(token))).toEqual({
|
||||
kind: 'item',
|
||||
owner_identity_id: 7,
|
||||
item_local_id: 42
|
||||
})
|
||||
})
|
||||
|
||||
test('rejects a token missing the leading ~', () => {
|
||||
expect(() => decodeShortId('DyU')).toThrow()
|
||||
})
|
||||
|
||||
test('rejects an unknown kind index', () => {
|
||||
expect(() => encodeShortId([99, 1])).toThrow()
|
||||
})
|
||||
|
||||
test('rejects an arity with no registered schema at that kind index', () => {
|
||||
// kind 0 is registered for arity 2 (item) and arity 1 (category), but not arity 3.
|
||||
expect(() => encodeShortId([0, 7, 8, 9])).toThrow()
|
||||
})
|
||||
|
||||
test('rejects an unknown kind name', () => {
|
||||
expect(() => serializeShortId({kind: 'not_a_kind'})).toThrow()
|
||||
})
|
||||
|
||||
test('rejects a missing field', () => {
|
||||
expect(() => serializeShortId({kind: 'item', owner_identity_id: 7})).toThrow()
|
||||
})
|
||||
|
||||
test('rejects a negative field value', () => {
|
||||
expect(() => encodeShortId([0, -1, 42])).toThrow()
|
||||
})
|
||||
|
||||
test('encodes a Domain-Qualified Short ID as "<domain>:<token>"', () => {
|
||||
expect(encodeDomainQualifiedShortId('toolsheddomain.tld', [0, 7, 42])).toBe('toolsheddomain.tld:~DyU')
|
||||
})
|
||||
|
||||
test('decodes a Domain-Qualified Short ID back into its domain and ints', () => {
|
||||
const decoded = decodeDomainQualifiedShortId('toolsheddomain.tld:~DyU')
|
||||
expect(decoded.domain).toBe('toolsheddomain.tld')
|
||||
expect(decoded.token).toBe('~DyU')
|
||||
expect(decoded.ints).toEqual([0, 7, 42])
|
||||
})
|
||||
|
||||
test('round-trips a Domain-Qualified Short ID through encode/decode', () => {
|
||||
const named = {kind: 'storage_location', owner_identity_id: 3, storage_location_id: 1000}
|
||||
const encoded = encodeDomainQualifiedShortId('example.com', serializeShortId(named))
|
||||
const {domain, ints} = decodeDomainQualifiedShortId(encoded)
|
||||
expect(domain).toBe('example.com')
|
||||
expect(deserializeShortId(ints)).toEqual(named)
|
||||
})
|
||||
|
||||
test('rejects a Domain-Qualified Short ID missing the "~" after the domain', () => {
|
||||
expect(() => decodeDomainQualifiedShortId('toolsheddomain.tldDyU')).toThrow()
|
||||
expect(() => decodeDomainQualifiedShortId('~DyU')).toThrow()
|
||||
})
|
||||
|
||||
test('isDomainQualifiedShortId tells a "<domain>:~token" apart from a bare token', () => {
|
||||
expect(isDomainQualifiedShortId('toolsheddomain.tld:~DyU')).toBe(true)
|
||||
expect(isDomainQualifiedShortId('~DyU')).toBe(false)
|
||||
expect(isDomainQualifiedShortId('alice@example.com:i42')).toBe(false)
|
||||
expect(isDomainQualifiedShortId('not a short id at all')).toBe(false)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue