feat(blog): limit rapid repeat views
This commit is contained in:
3 files changed
+85
-2
No files matched your search
@@ -5,10 +5,12 @@ import test from 'node:test';
|
||||
import fg from 'fast-glob';
|
||||
import matter from 'gray-matter';
|
||||
import {
|
||||
articleViewCooldownMs,
|
||||
bindLiveCounters,
|
||||
fetchCounterStats,
|
||||
readLikedSlugs,
|
||||
reportArticleView,
|
||||
reserveArticleView,
|
||||
updateLike,
|
||||
} from '../src/scripts/likes-views.ts';
|
||||
|
||||
@@ -139,6 +141,41 @@ test('one article navigation increments once while cards only retrieve statistic
|
||||
assert.deepEqual([...readLikedSlugs(storage)], []);
|
||||
});
|
||||
|
||||
test('a short per-tab cooldown suppresses back-navigation duplicates and then expires', async () => {
|
||||
const likeStorage = new FakeStorage();
|
||||
const viewStorage = new FakeStorage();
|
||||
const calls = [];
|
||||
const fetcher = async (url) => {
|
||||
calls.push(url);
|
||||
return response(url.endsWith('/views')
|
||||
? { views: 21 }
|
||||
: { 'shared-slug': { likes: 4, views: 20 } });
|
||||
};
|
||||
|
||||
await bindLiveCounters(new FakeRoot([new FakeCounter('shared-slug', { trackView: true })]), fetcher, likeStorage, viewStorage);
|
||||
await bindLiveCounters(new FakeRoot([new FakeCounter('shared-slug', { trackView: true })]), fetcher, likeStorage, viewStorage);
|
||||
assert.equal(calls.filter((url) => url.endsWith('/views')).length, 1);
|
||||
|
||||
const clockStorage = new FakeStorage();
|
||||
assert.equal(reserveArticleView('shared-slug', clockStorage, 10_000), true);
|
||||
assert.equal(reserveArticleView('shared-slug', clockStorage, 10_000 + articleViewCooldownMs - 1), false);
|
||||
assert.equal(reserveArticleView('shared-slug', clockStorage, 10_000 + articleViewCooldownMs), true);
|
||||
assert.equal(reserveArticleView('different-slug', clockStorage, 10_000 + articleViewCooldownMs), true);
|
||||
});
|
||||
|
||||
test('invalid or unavailable session storage fails open without persisting an identifier', () => {
|
||||
const corrupt = new FakeStorage();
|
||||
corrupt.setItem('aia-view-cooldowns-v1', '{broken');
|
||||
assert.equal(reserveArticleView('article', corrupt, 10_000), true);
|
||||
assert.deepEqual(JSON.parse(corrupt.getItem('aia-view-cooldowns-v1')), { article: 10_000 });
|
||||
|
||||
const blocked = {
|
||||
getItem() { throw new Error('blocked'); },
|
||||
setItem() { throw new Error('blocked'); },
|
||||
};
|
||||
assert.equal(reserveArticleView('article', blocked, 10_000), true);
|
||||
});
|
||||
|
||||
test('API failures preserve rendering and expose localized inline status', async () => {
|
||||
const counter = new FakeCounter('article', { trackView: true, lang: 'ar' });
|
||||
const root = new FakeRoot([counter]);
|
||||
|
||||
Reference in new issue
Block a user