mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-05 17:33:31 -04:00
no longer initializes embeddings model on startup
This commit is contained in:
@@ -496,3 +496,149 @@ describe('Timezone-aware daily verse system', () => {
|
||||
// See /api/submit-completion for the unique constraint enforcement
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Streak walk-back logic — mirrors /api/streak/+server.ts
|
||||
// UTC is NEVER used for date comparison; all walk-back is pure string arithmetic.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function prevDay(dateStr: string): string {
|
||||
const d = new Date(dateStr + 'T00:00:00Z');
|
||||
d.setUTCDate(d.getUTCDate() - 1);
|
||||
return d.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function calcStreak(completedDates: Set<string>, localDate: string): number {
|
||||
let streak = 0;
|
||||
let cursor = localDate;
|
||||
while (completedDates.has(cursor)) {
|
||||
streak++;
|
||||
cursor = prevDay(cursor);
|
||||
}
|
||||
return streak < 2 ? 0 : streak;
|
||||
}
|
||||
|
||||
describe('Streak walk-back — local time always used, UTC never', () => {
|
||||
test('prevDay uses UTC arithmetic, never server local time', () => {
|
||||
// Regardless of server timezone, prevDay("2024-03-10") must always be "2024-03-09"
|
||||
// (including across DST boundaries where local midnight ≠ UTC midnight)
|
||||
expect(prevDay('2024-03-10')).toBe('2024-03-09'); // DST spring-forward in US
|
||||
expect(prevDay('2024-11-03')).toBe('2024-11-02'); // DST fall-back in US
|
||||
expect(prevDay('2024-01-01')).toBe('2023-12-31'); // year boundary
|
||||
expect(prevDay('2024-03-01')).toBe('2024-02-29'); // leap year
|
||||
expect(prevDay('2023-03-01')).toBe('2023-02-28'); // non-leap year
|
||||
});
|
||||
|
||||
test('UTC+9 user: plays at 01:00 local (still previous UTC date) — streak uses local date', () => {
|
||||
// Scenario: It is 2024-01-16 01:00 in Tokyo (UTC+9) = 2024-01-15 16:00 UTC.
|
||||
// The verse served is for 2024-01-15 (UTC), but the user's LOCAL date is 2024-01-16.
|
||||
// Completions are stored as the user's local date (as returned by dailyVerse.date
|
||||
// which is set from the client's new Date().toLocaleDateString("en-CA")).
|
||||
// The streak walk-back must use local dates, not UTC dates.
|
||||
|
||||
// Four consecutive local dates for a Tokyo user
|
||||
const completedLocalDates = new Set(['2024-01-13', '2024-01-14', '2024-01-15', '2024-01-16']);
|
||||
|
||||
// If we (incorrectly) walked back from the UTC date "2024-01-15" instead of
|
||||
// the local date "2024-01-16", we would miss the most recent completion.
|
||||
const wrongStreakIfUTC = calcStreak(completedLocalDates, '2024-01-15');
|
||||
const correctStreakWithLocalDate = calcStreak(completedLocalDates, '2024-01-16');
|
||||
|
||||
// UTC walk-back misses the local "2024-01-16" entry → only finds 3 consecutive days
|
||||
// (but actually it finds 2024-01-15, 2024-01-14, 2024-01-13 = 3, returned as 3)
|
||||
// The point is it does NOT include 2024-01-16 which is "today" for the user.
|
||||
expect(wrongStreakIfUTC).toBe(3); // stale — missing today's local entry
|
||||
|
||||
// Local walk-back correctly finds all four entries
|
||||
expect(correctStreakWithLocalDate).toBe(4);
|
||||
});
|
||||
|
||||
test('UTC-8 user: plays at 23:00 local (next UTC date) — streak uses local date', () => {
|
||||
// Scenario: It is 2024-01-15 23:00 in Los Angeles (UTC-8) = 2024-01-16 07:00 UTC.
|
||||
// The verse served is for 2024-01-16 (UTC), but the user's LOCAL date is still 2024-01-15.
|
||||
// Completion is stored as local date "2024-01-15".
|
||||
// Walk-back from "2024-01-15" (local) must find it; walk-back from "2024-01-16" (UTC)
|
||||
// would NOT find it (the entry is stored as "2024-01-15").
|
||||
|
||||
const completedLocalDates = new Set(['2024-01-12', '2024-01-13', '2024-01-14', '2024-01-15']);
|
||||
|
||||
// If we (incorrectly) walked back from the UTC date "2024-01-16" instead of
|
||||
// the local date "2024-01-15", "2024-01-16" is not in the set → streak = 0.
|
||||
const wrongStreakIfUTC = calcStreak(completedLocalDates, '2024-01-16');
|
||||
const correctStreakWithLocalDate = calcStreak(completedLocalDates, '2024-01-15');
|
||||
|
||||
expect(wrongStreakIfUTC).toBe(0); // broken — UTC date not in DB
|
||||
expect(correctStreakWithLocalDate).toBe(4);
|
||||
});
|
||||
|
||||
test('UTC+13 user (Samoa): local date is two days ahead of UTC-11', () => {
|
||||
// Extreme case: UTC+13 user on 2024-01-16 local = 2024-01-15 UTC.
|
||||
const completedLocalDates = new Set(['2024-01-14', '2024-01-15', '2024-01-16']);
|
||||
|
||||
expect(calcStreak(completedLocalDates, '2024-01-14')).toBe(0); // only 1 day (< 2)
|
||||
expect(calcStreak(completedLocalDates, '2024-01-16')).toBe(3); // correct local streak
|
||||
expect(calcStreak(completedLocalDates, '2024-01-15')).toBe(0); // UTC date misses Jan 16
|
||||
});
|
||||
|
||||
test('streak is 0 when today is missing even if yesterday exists', () => {
|
||||
// User missed today — streak must reset regardless of timezone
|
||||
const completedLocalDates = new Set(['2024-01-12', '2024-01-13', '2024-01-14']);
|
||||
// "Today" is 2024-01-16 — they missed the 15th and 16th
|
||||
expect(calcStreak(completedLocalDates, '2024-01-16')).toBe(0);
|
||||
});
|
||||
|
||||
test('streak suppressed below 2 — single day returns 0', () => {
|
||||
const completedLocalDates = new Set(['2024-01-15']);
|
||||
expect(calcStreak(completedLocalDates, '2024-01-15')).toBe(0);
|
||||
});
|
||||
|
||||
test('DB entries with local date store correctly alongside UTC completion timestamp', async () => {
|
||||
// Verify DB round-trip: storing completions with local dates (not UTC)
|
||||
// ensures streak calculation with local dates always works.
|
||||
const userId = 'tz-test-user-' + crypto.randomUUID();
|
||||
|
||||
// UTC+9 scenario: played at 01:00 local on Jan 16 (= UTC Jan 15)
|
||||
// Local date is Jan 16, stored as "2024-01-16" in the DB.
|
||||
const completions = [
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
anonymousId: userId,
|
||||
date: '2024-01-14', // local date
|
||||
guessCount: 2,
|
||||
completedAt: new Date('2024-01-13T15:00:00Z'), // UTC
|
||||
},
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
anonymousId: userId,
|
||||
date: '2024-01-15', // local date
|
||||
guessCount: 3,
|
||||
completedAt: new Date('2024-01-14T15:00:00Z'), // UTC
|
||||
},
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
anonymousId: userId,
|
||||
date: '2024-01-16', // local date — UTC equivalent is 2024-01-15
|
||||
guessCount: 1,
|
||||
completedAt: new Date('2024-01-15T16:00:00Z'), // 01:00 Tokyo time
|
||||
},
|
||||
];
|
||||
|
||||
await db.insert(dailyCompletions).values(completions);
|
||||
|
||||
const rows = await db
|
||||
.select({ date: dailyCompletions.date })
|
||||
.from(dailyCompletions)
|
||||
.where(eq(dailyCompletions.anonymousId, userId));
|
||||
|
||||
const storedDates = new Set(rows.map((r) => r.date));
|
||||
|
||||
// Walk-back from LOCAL date "2024-01-16" finds all three entries
|
||||
expect(calcStreak(storedDates, '2024-01-16')).toBe(3);
|
||||
|
||||
// Walk-back from UTC date "2024-01-15" misses the "2024-01-16" local entry
|
||||
// (demonstrates the bug that was fixed: UTC walk-back gives wrong result)
|
||||
expect(calcStreak(storedDates, '2024-01-15')).toBe(0);
|
||||
|
||||
await db.delete(dailyCompletions).where(eq(dailyCompletions.anonymousId, userId));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user