101 lines
3.3 KiB
JavaScript
101 lines
3.3 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const { assertExpectedTiktokAccount, refreshTiktokTokens } = require('../lib/tiktok');
|
|
|
|
test('accepts the configured TikTok account', () => {
|
|
assert.doesNotThrow(() => assertExpectedTiktokAccount('greenlens-id', 'greenlens-id'));
|
|
});
|
|
|
|
test('rejects deployments without an expected GreenLens account', () => {
|
|
assert.throws(
|
|
() => assertExpectedTiktokAccount('greenlens-id', ''),
|
|
(error) => error.code === 'TIKTOK_ACCOUNT_NOT_CONFIGURED' && error.status === 500,
|
|
);
|
|
});
|
|
|
|
test('rejects a TikTok account that belongs to another brand', () => {
|
|
assert.throws(
|
|
() => assertExpectedTiktokAccount('qrmaster-id', 'greenlens-id'),
|
|
(error) => error.code === 'TIKTOK_ACCOUNT_MISMATCH' && error.status === 409,
|
|
);
|
|
});
|
|
|
|
test('expired refresh tokens require reconnecting before contacting TikTok', async () => {
|
|
const row = {
|
|
account_key: 'greenlens',
|
|
open_id: 'greenlens-id',
|
|
access_token: 'expired-access',
|
|
refresh_token: 'expired-refresh',
|
|
access_token_expires_at: new Date(Date.now() - 60_000),
|
|
refresh_token_expires_at: new Date(Date.now() - 1_000),
|
|
};
|
|
const db = {
|
|
query: async (sql, params) => ({ rows: sql.startsWith('SELECT') && params[0] === 'greenlens' ? [row] : [] }),
|
|
};
|
|
|
|
await assert.rejects(
|
|
refreshTiktokTokens(db, { clientKey: 'client', clientSecret: 'secret' }),
|
|
(error) => error.code === 'TIKTOK_RECONNECT_REQUIRED' && error.status === 401,
|
|
);
|
|
});
|
|
|
|
test('concurrent refreshes share one provider request and persist rotated tokens', async () => {
|
|
let row = {
|
|
account_key: 'greenlens',
|
|
open_id: 'greenlens-id',
|
|
access_token: 'old-access',
|
|
refresh_token: 'old-refresh',
|
|
scope: 'video.upload',
|
|
access_token_expires_at: new Date(Date.now() - 1_000),
|
|
refresh_token_expires_at: new Date(Date.now() + 60_000),
|
|
};
|
|
const db = {
|
|
query: async (sql, params) => {
|
|
if (sql.startsWith('SELECT')) return { rows: params[0] === 'greenlens' ? [row] : [] };
|
|
if (sql.startsWith('INSERT')) {
|
|
row = {
|
|
...row,
|
|
account_key: params[0],
|
|
open_id: params[1],
|
|
access_token: params[2],
|
|
refresh_token: params[3],
|
|
scope: params[4],
|
|
access_token_expires_at: params[5],
|
|
refresh_token_expires_at: params[6],
|
|
};
|
|
}
|
|
return { rows: [], rowCount: 1 };
|
|
},
|
|
};
|
|
const originalFetch = global.fetch;
|
|
let providerCalls = 0;
|
|
global.fetch = async () => {
|
|
providerCalls += 1;
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
return {
|
|
ok: true,
|
|
json: async () => ({
|
|
open_id: 'greenlens-id',
|
|
access_token: 'new-access',
|
|
refresh_token: 'rotated-refresh',
|
|
scope: 'video.upload',
|
|
expires_in: 86_400,
|
|
refresh_expires_in: 31_536_000,
|
|
}),
|
|
};
|
|
};
|
|
|
|
try {
|
|
const [first, second] = await Promise.all([
|
|
refreshTiktokTokens(db, { clientKey: 'client', clientSecret: 'secret' }),
|
|
refreshTiktokTokens(db, { clientKey: 'client', clientSecret: 'secret' }),
|
|
]);
|
|
assert.equal(providerCalls, 1);
|
|
assert.equal(first.access_token, 'new-access');
|
|
assert.equal(second.refresh_token, 'rotated-refresh');
|
|
} finally {
|
|
global.fetch = originalFetch;
|
|
}
|
|
});
|