Build the agent-native compute CRM platform
CI / verify (push) Successful in 3m6s

This commit is contained in:
2026-08-13 01:39:01 -07:00
parent bfd2f8d95a
commit 853bde2265
160 changed files with 61812 additions and 483 deletions
+90
View File
@@ -0,0 +1,90 @@
import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import {
collectNotionPages,
createNotionOAuthAttempt,
flattenNotionProperty,
notionAuthorizationUrl,
notionConnectionMetadata,
verifyNotionOAuthAttempt,
} from '../src/services/notion';
describe('Notion OAuth decisions', () => {
it('uses one-time state and browser binding without inventing unsupported PKCE parameters', () => {
let byte = 0;
const attempt = createNotionOAuthAttempt(
new Date('2026-08-13T12:00:00.000Z'),
(size) => Buffer.alloc(size, byte += 1),
);
assert.notEqual(attempt.state, attempt.verifier);
assert.notEqual(attempt.stateHash, attempt.state);
assert.notEqual(attempt.verifierHash, attempt.verifier);
assert.equal(verifyNotionOAuthAttempt(
attempt.verifier,
attempt.verifierHash,
attempt.expiresAt,
new Date('2026-08-13T12:09:59.000Z'),
), true);
assert.equal(verifyNotionOAuthAttempt('tampered', attempt.verifierHash, attempt.expiresAt), false);
assert.equal(verifyNotionOAuthAttempt(
attempt.verifier,
attempt.verifierHash,
attempt.expiresAt,
new Date('2026-08-13T12:10:00.000Z'),
), false);
const url = new URL(notionAuthorizationUrl({
clientId: 'client-id',
redirectUri: 'https://pig.example/api/imports/notion/oauth/callback',
state: attempt.state,
}));
assert.equal(url.searchParams.get('state'), attempt.state);
assert.equal(url.searchParams.has('code_challenge'), false);
assert.equal(url.searchParams.has('code_verifier'), false);
});
it('redacts every credential-shaped field from connection metadata', () => {
const storedConnection = {
id: 'connection-id',
workspaceId: 'workspace-id',
workspaceName: 'Sales',
workspaceIcon: null,
createdAt: new Date('2026-08-13T12:00:00.000Z'),
credentialsEncrypted: 'v1.secret.envelope',
accessToken: 'never-return',
};
const metadata = notionConnectionMetadata(storedConnection);
const serialized = JSON.stringify(metadata);
assert.equal(serialized.includes('never-return'), false);
assert.equal(serialized.includes('envelope'), false);
});
});
describe('Notion pagination and flattening decisions', () => {
it('follows cursors in order and stops after the declared import bound', async () => {
const cursors: Array<string | undefined> = [];
const rows = await collectNotionPages(async (cursor) => {
cursors.push(cursor);
return cursor
? { results: [{ id: '2' }, { id: '3' }], has_more: false, next_cursor: null }
: { results: [{ id: '1' }], has_more: true, next_cursor: 'next' };
}, 2);
assert.deepEqual(cursors, [undefined, 'next']);
assert.deepEqual(rows.map((row) => row.id), ['1', '2']);
});
it('maps supported values explicitly and rejects unstable property types', () => {
assert.deepEqual(flattenNotionProperty({
type: 'title',
title: [{ plain_text: 'Acme' }, { plain_text: ' Compute' }],
}), { value: 'Acme Compute' });
assert.deepEqual(flattenNotionProperty({
type: 'date',
date: { start: '2026-09-01', end: '2026-09-30' },
}), { value: '2026-09-01/2026-09-30' });
assert.deepEqual(flattenNotionProperty({
type: 'formula',
formula: { type: 'number', number: 12.5 },
}), { value: '12.5' });
assert.match(flattenNotionProperty({ type: 'button', button: {} }).error ?? '', /stable tabular/);
});
});