Skip to content

Commit 4becfed

Browse files
committed
test(protect): add protocol backfill Simple variants for PG integration tests
Add 10 Simple protocol tests across 4 describe blocks: - jsonb_path_query: top-level, nested, deep nested, non-matching (4 tests) - jsonb_path_exists: nested path (1 test) - field access ->: chained text keys (1 test) - WHERE = equality: self-comparison, jpqf self-comparison, cross-row match, different value (4 tests)
1 parent 76ba943 commit 4becfed

File tree

1 file changed

+327
-0
lines changed

1 file changed

+327
-0
lines changed

packages/protect/__tests__/searchable-json-pg.test.ts

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,147 @@ describe('searchableJson postgres integration', () => {
322322

323323
expect(decrypted.data.metadata).toEqual(plaintextWithPath)
324324
}, 30000)
325+
326+
it('finds row by simple top-level path (Simple)', async () => {
327+
const plaintext = { role: 'path-tl-simple', extra: 'data' }
328+
329+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
330+
if (encrypted.failure) throw new Error(encrypted.failure.message)
331+
332+
const [inserted] = await sql`
333+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
334+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
335+
RETURNING id
336+
`
337+
338+
const queryResult = await protectClient.encryptQuery('$.role', {
339+
column: table.metadata,
340+
table: table,
341+
queryType: 'steVecSelector',
342+
returnType: 'composite-literal',
343+
})
344+
if (queryResult.failure) throw new Error(queryResult.failure.message)
345+
const selectorTerm = queryResult.data
346+
347+
const rows = await sql.unsafe(
348+
`SELECT id, (metadata).data as metadata
349+
FROM "protect-ci-jsonb" t,
350+
eql_v2.jsonb_path_query(t.metadata, '${selectorTerm}'::eql_v2_encrypted) as result
351+
WHERE t.test_run_id = '${TEST_RUN_ID}'`
352+
)
353+
354+
expect(rows.length).toBeGreaterThanOrEqual(1)
355+
const matchingRow = rows.find((r: any) => r.id === inserted.id)
356+
expect(matchingRow).toBeDefined()
357+
358+
const decrypted = await protectClient.decryptModel({ metadata: matchingRow!.metadata })
359+
if (decrypted.failure) throw new Error(decrypted.failure.message)
360+
expect(decrypted.data.metadata).toEqual(plaintext)
361+
}, 30000)
362+
363+
it('finds row by nested path (Simple)', async () => {
364+
const plaintext = { user: { email: 'nested-simple@test.com' }, type: 'nested-path-simple' }
365+
366+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
367+
if (encrypted.failure) throw new Error(encrypted.failure.message)
368+
369+
const [inserted] = await sql`
370+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
371+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
372+
RETURNING id
373+
`
374+
375+
const queryResult = await protectClient.encryptQuery('$.user.email', {
376+
column: table.metadata,
377+
table: table,
378+
queryType: 'steVecSelector',
379+
returnType: 'composite-literal',
380+
})
381+
if (queryResult.failure) throw new Error(queryResult.failure.message)
382+
const selectorTerm = queryResult.data
383+
384+
const rows = await sql.unsafe(
385+
`SELECT id, (metadata).data as metadata
386+
FROM "protect-ci-jsonb" t,
387+
eql_v2.jsonb_path_query(t.metadata, '${selectorTerm}'::eql_v2_encrypted) as result
388+
WHERE t.test_run_id = '${TEST_RUN_ID}'`
389+
)
390+
391+
expect(rows.length).toBeGreaterThanOrEqual(1)
392+
const matchingRow = rows.find((r: any) => r.id === inserted.id)
393+
expect(matchingRow).toBeDefined()
394+
395+
const decrypted = await protectClient.decryptModel({ metadata: matchingRow!.metadata })
396+
if (decrypted.failure) throw new Error(decrypted.failure.message)
397+
expect(decrypted.data.metadata).toEqual(plaintext)
398+
}, 30000)
399+
400+
it('finds with deep nested path (Simple)', async () => {
401+
const plaintext = { target: { nested: { value: 'deep-simple' } }, marker: 'jpq-deep-simple' }
402+
403+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
404+
if (encrypted.failure) throw new Error(encrypted.failure.message)
405+
406+
const [inserted] = await sql`
407+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
408+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
409+
RETURNING id
410+
`
411+
412+
const queryResult = await protectClient.encryptQuery('$.target.nested.value', {
413+
column: table.metadata,
414+
table: table,
415+
queryType: 'steVecSelector',
416+
returnType: 'composite-literal',
417+
})
418+
if (queryResult.failure) throw new Error(queryResult.failure.message)
419+
const selectorTerm = queryResult.data
420+
421+
const rows = await sql.unsafe(
422+
`SELECT id, (metadata).data as metadata
423+
FROM "protect-ci-jsonb" t,
424+
eql_v2.jsonb_path_query(t.metadata, '${selectorTerm}'::eql_v2_encrypted) as result
425+
WHERE t.test_run_id = '${TEST_RUN_ID}'`
426+
)
427+
428+
expect(rows.length).toBeGreaterThanOrEqual(1)
429+
const matchingRow = rows.find((r: any) => r.id === inserted.id)
430+
expect(matchingRow).toBeDefined()
431+
432+
const decrypted = await protectClient.decryptModel({ metadata: matchingRow!.metadata })
433+
if (decrypted.failure) throw new Error(decrypted.failure.message)
434+
expect(decrypted.data.metadata).toEqual(plaintext)
435+
}, 30000)
436+
437+
it('non-matching path returns zero rows (Simple)', async () => {
438+
const plaintext = { data: true, marker: 'jpq-nomatch-simple' }
439+
440+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
441+
if (encrypted.failure) throw new Error(encrypted.failure.message)
442+
443+
await sql`
444+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
445+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
446+
`
447+
448+
const queryResult = await protectClient.encryptQuery('$.missing.path', {
449+
column: table.metadata,
450+
table: table,
451+
queryType: 'steVecSelector',
452+
returnType: 'composite-literal',
453+
})
454+
if (queryResult.failure) throw new Error(queryResult.failure.message)
455+
const selectorTerm = queryResult.data
456+
457+
const rows = await sql.unsafe(
458+
`SELECT id, (metadata).data as metadata
459+
FROM "protect-ci-jsonb" t,
460+
eql_v2.jsonb_path_query(t.metadata, '${selectorTerm}'::eql_v2_encrypted) as result
461+
WHERE t.test_run_id = '${TEST_RUN_ID}'`
462+
)
463+
464+
expect(rows.length).toBe(0)
465+
}, 30000)
325466
})
326467

327468
// ─── Containment: @> term queries ──────────────────────────────────
@@ -1792,6 +1933,43 @@ describe('searchableJson postgres integration', () => {
17921933
expect(decrypted.data.metadata).toEqual(plaintext)
17931934
}, 30000)
17941935

1936+
it('returns true for nested path (Simple)', async () => {
1937+
const plaintext = { user: { email: 'pe-nested-simple@test.com' }, type: 'pe-nested-simple' }
1938+
1939+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
1940+
if (encrypted.failure) throw new Error(encrypted.failure.message)
1941+
1942+
const [inserted] = await sql`
1943+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
1944+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
1945+
RETURNING id
1946+
`
1947+
1948+
const queryResult = await protectClient.encryptQuery('$.user.email', {
1949+
column: table.metadata,
1950+
table: table,
1951+
queryType: 'steVecSelector',
1952+
returnType: 'composite-literal',
1953+
})
1954+
if (queryResult.failure) throw new Error(queryResult.failure.message)
1955+
const selectorTerm = queryResult.data
1956+
1957+
const rows = await sql.unsafe(
1958+
`SELECT id, (metadata).data as metadata
1959+
FROM "protect-ci-jsonb" t
1960+
WHERE eql_v2.jsonb_path_exists(t.metadata, '${selectorTerm}'::eql_v2_encrypted)
1961+
AND test_run_id = '${TEST_RUN_ID}'`
1962+
)
1963+
1964+
expect(rows.length).toBeGreaterThanOrEqual(1)
1965+
const matchingRow = rows.find((r: any) => r.id === inserted.id)
1966+
expect(matchingRow).toBeDefined()
1967+
1968+
const decrypted = await protectClient.decryptModel({ metadata: matchingRow!.metadata })
1969+
if (decrypted.failure) throw new Error(decrypted.failure.message)
1970+
expect(decrypted.data.metadata).toEqual(plaintext)
1971+
}, 30000)
1972+
17951973
it('returns false for unknown path (Simple)', async () => {
17961974
const plaintext = { exists: true, marker: 'pe-nomatch-simple' }
17971975

@@ -2631,6 +2809,29 @@ describe('searchableJson postgres integration', () => {
26312809
expect(rows[0].extracted).not.toBeNull()
26322810
}, 30000)
26332811

2812+
it('extracts nested field by chained text keys (Simple)', async () => {
2813+
const plaintext = { user: { email: 'fa-nested-simple@test.com' }, marker: 'fa-nested-simple' }
2814+
2815+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
2816+
if (encrypted.failure) throw new Error(encrypted.failure.message)
2817+
2818+
const [inserted] = await sql`
2819+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
2820+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
2821+
RETURNING id
2822+
`
2823+
2824+
const rows = await sql.unsafe(
2825+
`SELECT (t.metadata -> 'user') -> 'email' as extracted
2826+
FROM "protect-ci-jsonb" t
2827+
WHERE t.id = $1`,
2828+
[inserted.id]
2829+
)
2830+
2831+
expect(rows).toHaveLength(1)
2832+
expect(rows[0].extracted).not.toBeNull()
2833+
}, 30000)
2834+
26342835
it('returns null for missing field (Simple)', async () => {
26352836
const plaintext = { exists: true, marker: 'fa-s-miss' }
26362837

@@ -2779,5 +2980,131 @@ describe('searchableJson postgres integration', () => {
27792980

27802981
expect(rows).toHaveLength(0)
27812982
}, 30000)
2983+
2984+
it('-> extraction = self-comparison (Simple)', async () => {
2985+
const plaintext = { role: 'eq-self-s', marker: 'eq-self-s-marker' }
2986+
2987+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
2988+
if (encrypted.failure) throw new Error(encrypted.failure.message)
2989+
2990+
const [inserted] = await sql`
2991+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
2992+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
2993+
RETURNING id
2994+
`
2995+
2996+
const rows = await sql.unsafe(
2997+
`SELECT id
2998+
FROM "protect-ci-jsonb" t
2999+
WHERE t.metadata -> 'role' = t.metadata -> 'role'
3000+
AND t.id = $1`,
3001+
[inserted.id]
3002+
)
3003+
3004+
expect(rows).toHaveLength(1)
3005+
}, 30000)
3006+
3007+
it('jsonb_path_query_first = self-comparison (Simple)', async () => {
3008+
const plaintext = { role: 'eq-jpqf-s', marker: 'eq-jpqf-s-marker' }
3009+
3010+
const encrypted = await protectClient.encryptModel({ metadata: plaintext }, table)
3011+
if (encrypted.failure) throw new Error(encrypted.failure.message)
3012+
3013+
const [inserted] = await sql`
3014+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
3015+
VALUES (${sql.json(encrypted.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
3016+
RETURNING id
3017+
`
3018+
3019+
const queryResult = await protectClient.encryptQuery('$.role', {
3020+
column: table.metadata,
3021+
table: table,
3022+
queryType: 'steVecSelector',
3023+
returnType: 'composite-literal',
3024+
})
3025+
if (queryResult.failure) throw new Error(queryResult.failure.message)
3026+
const selectorTerm = queryResult.data
3027+
3028+
const rows = await sql.unsafe(
3029+
`SELECT id
3030+
FROM "protect-ci-jsonb" t
3031+
WHERE eql_v2.jsonb_path_query_first(t.metadata, $1::eql_v2_encrypted)
3032+
= eql_v2.jsonb_path_query_first(t.metadata, $1::eql_v2_encrypted)
3033+
AND t.id = $2`,
3034+
[selectorTerm, inserted.id]
3035+
)
3036+
3037+
expect(rows).toHaveLength(1)
3038+
}, 30000)
3039+
3040+
it('-> extraction = cross-row match (Simple)', async () => {
3041+
const plaintext1 = { role: 'eq-cross-s', marker: 'eq-cross-s-1' }
3042+
const plaintext2 = { role: 'eq-cross-s', marker: 'eq-cross-s-2' }
3043+
3044+
const enc1 = await protectClient.encryptModel({ metadata: plaintext1 }, table)
3045+
if (enc1.failure) throw new Error(enc1.failure.message)
3046+
const enc2 = await protectClient.encryptModel({ metadata: plaintext2 }, table)
3047+
if (enc2.failure) throw new Error(enc2.failure.message)
3048+
3049+
const [row1] = await sql`
3050+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
3051+
VALUES (${sql.json(enc1.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
3052+
RETURNING id
3053+
`
3054+
const [row2] = await sql`
3055+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
3056+
VALUES (${sql.json(enc2.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
3057+
RETURNING id
3058+
`
3059+
3060+
const rows = await sql.unsafe(
3061+
`SELECT t.id
3062+
FROM "protect-ci-jsonb" t
3063+
WHERE t.metadata -> 'role' = (
3064+
SELECT s.metadata -> 'role'
3065+
FROM "protect-ci-jsonb" s
3066+
WHERE s.id = $2
3067+
)
3068+
AND t.id = $1`,
3069+
[row1.id, row2.id]
3070+
)
3071+
3072+
expect(rows).toHaveLength(1)
3073+
}, 30000)
3074+
3075+
it('-> extraction != different value (Simple)', async () => {
3076+
const plaintext1 = { role: 'eq-diff-s-a', marker: 'eq-diff-s-1' }
3077+
const plaintext2 = { role: 'eq-diff-s-b', marker: 'eq-diff-s-2' }
3078+
3079+
const enc1 = await protectClient.encryptModel({ metadata: plaintext1 }, table)
3080+
if (enc1.failure) throw new Error(enc1.failure.message)
3081+
const enc2 = await protectClient.encryptModel({ metadata: plaintext2 }, table)
3082+
if (enc2.failure) throw new Error(enc2.failure.message)
3083+
3084+
const [row1] = await sql`
3085+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
3086+
VALUES (${sql.json(enc1.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
3087+
RETURNING id
3088+
`
3089+
const [row2] = await sql`
3090+
INSERT INTO "protect-ci-jsonb" (metadata, test_run_id)
3091+
VALUES (${sql.json(enc2.data.metadata)}::eql_v2_encrypted, ${TEST_RUN_ID})
3092+
RETURNING id
3093+
`
3094+
3095+
const rows = await sql.unsafe(
3096+
`SELECT t.id
3097+
FROM "protect-ci-jsonb" t
3098+
WHERE t.metadata -> 'role' = (
3099+
SELECT s.metadata -> 'role'
3100+
FROM "protect-ci-jsonb" s
3101+
WHERE s.id = $2
3102+
)
3103+
AND t.id = $1`,
3104+
[row1.id, row2.id]
3105+
)
3106+
3107+
expect(rows).toHaveLength(0)
3108+
}, 30000)
27823109
})
27833110
})

0 commit comments

Comments
 (0)