From b52479975c7d97ba6d19e057bf48995f274169cb Mon Sep 17 00:00:00 2001 From: Wanjohi Date: Sun, 11 Oct 2026 13:22:22 +0300 Subject: [PATCH] feat(trial): the free days are Saturday, Sunday and Monday Monday in place of Friday: the window is Saturday 00:00 to Tuesday 00:00, Helsinki time, and the refusal outside it says Saturday to Monday. --- apps/api/test/trial.test.ts | 29 +++++++++++++++++------------ packages/core/src/trial/index.ts | 19 ++++++++++--------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/apps/api/test/trial.test.ts b/apps/api/test/trial.test.ts index fce3d388..4f25fc1d 100644 --- a/apps/api/test/trial.test.ts +++ b/apps/api/test/trial.test.ts @@ -126,12 +126,12 @@ async function playedBefore( } describe('The weekend window', () => { - test('Friday 00:00 to Monday 00:00 on the trial’s clock', () => { + test('Saturday 00:00 to Tuesday 00:00 on the trial’s clock', () => { const closed = Trial.window(WEDNESDAY); expect(closed.open).toBe(false); // Summer time: midnight there is 21:00 UTC the day before. - expect(closed.start.toISOString()).toBe('2026-10-08T21:00:00.000Z'); - expect(closed.end.toISOString()).toBe('2026-10-11T21:00:00.000Z'); + expect(closed.start.toISOString()).toBe('2026-10-09T21:00:00.000Z'); + expect(closed.end.toISOString()).toBe('2026-10-12T21:00:00.000Z'); const open = Trial.window(SATURDAY); expect(open.open).toBe(true); @@ -141,13 +141,18 @@ describe('The weekend window', () => { test('a weekend with a clock change in it is still three calendar days', () => { // Summer time ends in the early hours of Sunday 25 October 2026. const w = Trial.window(new Date('2026-10-24T12:00:00Z')); - expect(w.start.toISOString()).toBe('2026-10-22T21:00:00.000Z'); - expect(w.end.toISOString()).toBe('2026-10-25T22:00:00.000Z'); + expect(w.start.toISOString()).toBe('2026-10-23T21:00:00.000Z'); + expect(w.end.toISOString()).toBe('2026-10-26T22:00:00.000Z'); }); - test('the last minute of Sunday is in; Monday 00:00 is out', () => { - expect(Trial.window(new Date('2026-10-11T20:59:00Z')).open).toBe(true); - expect(Trial.window(new Date('2026-10-11T21:00:00Z')).open).toBe(false); + test('Monday is in; Friday is not', () => { + expect(Trial.window(new Date('2026-10-12T12:00:00Z')).open).toBe(true); + expect(Trial.window(new Date('2026-10-09T12:00:00Z')).open).toBe(false); + }); + + test('the last minute of Monday is in; Tuesday 00:00 is out', () => { + expect(Trial.window(new Date('2026-10-12T20:59:00Z')).open).toBe(true); + expect(Trial.window(new Date('2026-10-12T21:00:00Z')).open).toBe(false); }); }); @@ -169,7 +174,7 @@ describe('POST /session on rented GPUs without a plan', () => { const s = await scene('trial-closed'); const { res, body } = await play(s); expect(res.status).toBe(429); - expect(body.message).toContain('Friday to Sunday'); + expect(body.message).toContain('Saturday to Monday'); }); test('two hours used this weekend: 429', async () => { @@ -179,7 +184,7 @@ describe('POST /session on rented GPUs without a plan', () => { s.box.id, s.gameId, s.owner.linkedAccountId, - new Date('2026-10-09T10:00:00Z'), + new Date('2026-10-10T08:00:00Z'), 120 ); const { res, body } = await play(s); @@ -286,9 +291,9 @@ describe('Stopping a trial run', () => { }); test('when the window closes: stopped, for the window', async () => { - at(new Date('2026-10-11T21:05:00Z')); + at(new Date('2026-10-12T21:05:00Z')); const s = await scene('stop-window'); - const id = await liveTrialRun(s, new Date('2026-10-11T20:30:00Z')); + const id = await liveTrialRun(s, new Date('2026-10-12T20:30:00Z')); const stops = (await jobs(s)).filter((j) => j.kind === 'session.stop'); expect(stops).toEqual([ { kind: 'session.stop', sessionId: id, boxId: s.box.id, reason: 'window' } diff --git a/packages/core/src/trial/index.ts b/packages/core/src/trial/index.ts index d6cf4d84..644b4eec 100644 --- a/packages/core/src/trial/index.ts +++ b/packages/core/src/trial/index.ts @@ -18,8 +18,9 @@ import { TrialClaimTable } from './trial.sql.js'; * * Three rules and nothing else: * - * - **When.** Friday 00:00 to Monday 00:00, on the clock of the place the - * hardware is run from. Outside that, a trial run is refused, and one still + * - **When.** Saturday 00:00 to Tuesday 00:00 — Saturday, Sunday and + * Monday, Monday in place of Friday since 2026-10-11 — on the clock of the + * place the hardware is run from. Outside that, a trial run is refused, and one still * going when it closes is stopped. * - **How much.** {@link HOURS} of live time per window, across every run. * Unlike a plan's allowance, this one stops a run that reaches it: the @@ -104,14 +105,14 @@ export namespace Trial { /** The weekend window an instant is in, or the next one if it is in none. */ export function window(at: Date = now()): Window { const today = localDay(at); - const sinceFriday = (today.weekday - 5 + 7) % 7; // Fri 0, Sat 1, Sun 2, Mon 3 … - const open = sinceFriday <= 2; - const friday = addDays(today, open ? -sinceFriday : 7 - sinceFriday); - const monday = addDays(friday, 3); + const sinceSaturday = (today.weekday - 6 + 7) % 7; // Sat 0, Sun 1, Mon 2, Tue 3 … + const open = sinceSaturday <= 2; + const saturday = addDays(today, open ? -sinceSaturday : 7 - sinceSaturday); + const tuesday = addDays(saturday, 3); return { open, - start: midnight(friday.year, friday.month, friday.day), - end: midnight(monday.year, monday.month, monday.day) + start: midnight(saturday.year, saturday.month, saturday.day), + end: midnight(tuesday.year, tuesday.month, tuesday.day) }; } @@ -190,7 +191,7 @@ export namespace Trial { throw new VisibleError( 'rate_limit', ErrorCodes.RateLimit.QUOTA_EXCEEDED, - `Free play on Nestri GPUs is Friday to Sunday. The next window opens ${when(w.start)}.` + `Free play on Nestri GPUs is Saturday to Monday. The next window opens ${when(w.start)}.` ); } if ((await usedSeconds(input.userId, w, at)) >= SECONDS) {