From caa7143f49bbc63c2b2401bf44ff220e85ab5ca2 Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Sat, 7 Nov 2026 22:47:11 +0800 Subject: [PATCH 1/2] fix: clamp imported 0-minute sessions to the minimum, not the 30-minute default parseInt(duration) || 30 treated 0 as falsy, so a 0-minute session in an imported backup became 30 minutes, inflating study hours, the productivity score and the Century Club achievement. Only fall back to the 30-minute default when the value is not a number; zero and negative values clamp to the 1-minute minimum like any other out-of-range input. --- CHANGELOG.md | 1 + assets/js/storage.js | 3 ++- tests/storage.test.js | 9 ++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e49951d..6caad05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and - 52 unit tests for dates, streaks, the productivity score, storage, import/export and achievement rules, run in four timezones on every pull request and push. ### Fixed +- Importing a backup with a 0-minute session no longer turns it into 30 minutes; zero and negative durations are clamped to 1, the same as any other out-of-range value. - A brand-new account with nothing logged showed a productivity score of 20/100. It now shows 0 until the first session is logged. ### Removed diff --git a/assets/js/storage.js b/assets/js/storage.js index b7e15fc..e379144 100644 --- a/assets/js/storage.js +++ b/assets/js/storage.js @@ -661,7 +661,8 @@ class StorageManager { g.progress = Math.min(100, Math.max(0, parseInt(g.progress, 10) || 0)); }); parsed.activities.forEach(a => { - a.duration = Math.max(1, parseInt(a.duration, 10) || 30); + const duration = parseInt(a.duration, 10); + a.duration = Math.max(1, Number.isNaN(duration) ? 30 : duration); }); this.saveUniverse(parsed); diff --git a/tests/storage.test.js b/tests/storage.test.js index 232f27e..9232b1e 100644 --- a/tests/storage.test.js +++ b/tests/storage.test.js @@ -176,7 +176,9 @@ describe('import and export', () => { projects: [{ id: 'p1', progress: '42' }], activities: [ { id: 'a1', duration: -10 }, - { id: 'a2', duration: 'x' } + { id: 'a2', duration: 'x' }, + { id: 'a3', duration: 0 }, + { id: 'a4' } ] }) ); @@ -188,6 +190,11 @@ describe('import and export', () => { assert.equal(u.projects[0].progress, 42); assert.deepEqual(u.projects[0].technologies, []); for (const a of u.activities) assert.ok(a.duration >= 1, `duration ${a.duration}`); + // -10 -> 1 (minimum), 0 -> 1, non-numeric/missing -> 30 (default) + assert.deepEqual( + u.activities.map(a => a.duration), + [1, 30, 1, 30] + ); }); // Regression for D-009: a backup without a profile used to become "Alex Rivera". From 24b35b5410e794d2491df8cbc477cf379ce290af Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:22:18 +0800 Subject: [PATCH 2/2] test: give imported activities valid dates so they survive the new import date filter Upstream 344b1fc made importJSON drop activities whose date is not a valid ISO date, so the four dateless activities in this fixture were all filtered out on the merge commit and CI saw [] instead of [1, 30, 1, 30]. The duration expectations are unchanged. --- tests/storage.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/storage.test.js b/tests/storage.test.js index 02d1770..d38d4dc 100644 --- a/tests/storage.test.js +++ b/tests/storage.test.js @@ -174,11 +174,13 @@ describe('import and export', () => { { id: 's2', progress: -5 } ], projects: [{ id: 'p1', progress: '42' }], + // Every activity needs a valid date: imports drop dateless entries + // (see the malformed-dates security test below). activities: [ - { id: 'a1', duration: -10 }, - { id: 'a2', duration: 'x' }, - { id: 'a3', duration: 0 }, - { id: 'a4' } + { id: 'a1', date: '2026-11-01', duration: -10 }, + { id: 'a2', date: '2026-11-02', duration: 'x' }, + { id: 'a3', date: '2026-11-03', duration: 0 }, + { id: 'a4', date: '2026-11-04' } ] }) );