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.
This commit is contained in:
Wanjohi
2026-10-11 13:52:56 +03:00
parent cc8d1c1423
commit b52479975c
2 changed files with 27 additions and 21 deletions
+17 -12
View File
@@ -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' }
+10 -9
View File
@@ -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) {