11import { strict as assert } from "node:assert" ;
2- import { db } from "../db/index.js" ;
3- import { advanceGoalInstances } from "./advance.js" ;
4- import dayjs from "./dateUtils.js" ;
2+ import process from "node:process" ;
3+
4+ // Safety guard: advanceGoalInstances() scans every user, marks their
5+ // pending instances as 'missed', and calls scheduleGoal against the real
6+ // Google Calendar API. Running these tests against goaly.db would corrupt
7+ // real user data. The `test` / `ci` tasks in deno.json set DB_PATH=:memory:
8+ // — this check refuses to run if someone invokes `deno test` directly
9+ // without overriding the default.
10+ if ( ! process . env . DB_PATH || process . env . DB_PATH === "goaly.db" ) {
11+ throw new Error (
12+ "advance.test.js refuses to run against the default DB_PATH. " +
13+ "Use `deno task test` (which sets DB_PATH=:memory:) or export DB_PATH yourself." ,
14+ ) ;
15+ }
16+
17+ const { db } = await import ( "../db/index.js" ) ;
18+ const { advanceGoalInstances } = await import ( "./advance.js" ) ;
19+ const dayjs = ( await import ( "./dateUtils.js" ) ) . default ;
20+
21+ // Stand-in for scheduleGoal so tests don't hit the real Google Calendar API
22+ // with fake refresh tokens. The unit under test here is the
23+ // mark-missed-and-tick-tracking logic, not the scheduler itself.
24+ const noopScheduler = async ( ) => { } ;
525
626/**
727 * @typedef {Object } TestUser
@@ -108,7 +128,7 @@ function createInstance(goal, startIso, endIso, status) {
108128}
109129
110130Deno . test ( "advanceGoalInstances - marks past-due instances as missed" , async ( ) => {
111- const { user } = await setupUser ( ) ;
131+ const { user } = setupUser ( ) ;
112132 const goal = createGoal ( user , "Reading Time" , 3 , "afternoon" ) ;
113133
114134 // Create 2 past-due pending instances
@@ -126,7 +146,7 @@ Deno.test("advanceGoalInstances - marks past-due instances as missed", async ()
126146 "Should have 2 pending instances before advancement" ,
127147 ) ;
128148
129- const result = await advanceGoalInstances ( ) ;
149+ const result = await advanceGoalInstances ( noopScheduler ) ;
130150
131151 instances = db . prepare ( "SELECT * FROM goal_instances WHERE goal_id = ?" ) . all (
132152 goal . id ,
@@ -143,7 +163,7 @@ Deno.test("advanceGoalInstances - marks past-due instances as missed", async ()
143163} ) ;
144164
145165Deno . test ( "advanceGoalInstances - skips future pending instances" , async ( ) => {
146- const { user } = await setupUser ( ) ;
166+ const { user } = setupUser ( ) ;
147167 const goal = createGoal ( user , "Future Goal" , 3 , "afternoon" ) ;
148168
149169 const pastTime = dayjs . utc ( ) . subtract ( 1 , "day" ) . toISOString ( ) ;
@@ -156,7 +176,7 @@ Deno.test("advanceGoalInstances - skips future pending instances", async () => {
156176 . toISOString ( ) ;
157177 createInstance ( goal , futureTime , futureEndTime ) ;
158178
159- const result = await advanceGoalInstances ( ) ;
179+ const result = await advanceGoalInstances ( noopScheduler ) ;
160180
161181 const instances = db . prepare ( "SELECT * FROM goal_instances WHERE goal_id = ?" )
162182 . all ( goal . id ) ;
@@ -181,16 +201,16 @@ Deno.test("advanceGoalInstances - skips future pending instances", async () => {
181201} ) ;
182202
183203Deno . test ( "advanceGoalInstances - handles goals with no pending instances" , async ( ) => {
184- const { user } = await setupUser ( ) ;
204+ const { user } = setupUser ( ) ;
185205 createGoal ( user , "No Pending Goal" , 3 , "afternoon" ) ;
186206
187- const result = await advanceGoalInstances ( ) ;
207+ const result = await advanceGoalInstances ( noopScheduler ) ;
188208 assert . equal ( result . advanced , 0 , "Should not have advanced any goals" ) ;
189209 assert . equal ( result . missed , 0 , "Should not have missed any instances" ) ;
190210} ) ;
191211
192212Deno . test ( "advanceGoalInstances - marks all past-due instances as missed" , async ( ) => {
193- const { user } = await setupUser ( ) ;
213+ const { user } = setupUser ( ) ;
194214 const goal = createGoal ( user , "Many Past Goal" , 5 , "afternoon" ) ;
195215
196216 const pastTime = dayjs . utc ( ) . subtract ( 2 , "day" ) . toISOString ( ) ;
@@ -200,7 +220,7 @@ Deno.test("advanceGoalInstances - marks all past-due instances as missed", async
200220 createInstance ( goal , pastTime , pastEndTime ) ;
201221 }
202222
203- const result = await advanceGoalInstances ( ) ;
223+ const result = await advanceGoalInstances ( noopScheduler ) ;
204224
205225 const instances = db . prepare ( "SELECT * FROM goal_instances WHERE goal_id = ?" )
206226 . all ( goal . id ) ;
@@ -212,7 +232,7 @@ Deno.test("advanceGoalInstances - marks all past-due instances as missed", async
212232} ) ;
213233
214234Deno . test ( "advanceGoalInstances - multiple goals for same user" , async ( ) => {
215- const { user } = await setupUser ( ) ;
235+ const { user } = setupUser ( ) ;
216236 const goal1 = createGoal ( user , "Goal 1" , 2 , "morning" ) ;
217237 const goal2 = createGoal ( user , "Goal 2" , 2 , "afternoon" ) ;
218238
@@ -222,7 +242,7 @@ Deno.test("advanceGoalInstances - multiple goals for same user", async () => {
222242 createInstance ( goal1 , pastTime , pastEndTime ) ;
223243 createInstance ( goal2 , pastTime , pastEndTime ) ;
224244
225- const result = await advanceGoalInstances ( ) ;
245+ const result = await advanceGoalInstances ( noopScheduler ) ;
226246
227247 const instances1 = db . prepare (
228248 "SELECT * FROM goal_instances WHERE goal_id = ?" ,
@@ -243,7 +263,7 @@ Deno.test("advanceGoalInstances - multiple goals for same user", async () => {
243263} ) ;
244264
245265Deno . test ( "advanceGoalInstances - does not mark non-pending instances as missed" , async ( ) => {
246- const { user } = await setupUser ( ) ;
266+ const { user } = setupUser ( ) ;
247267 const goal = createGoal ( user , "Non-Pending Goal" , 3 , "afternoon" ) ;
248268
249269 const pastTime = dayjs . utc ( ) . subtract ( 1 , "day" ) . toISOString ( ) ;
@@ -254,7 +274,7 @@ Deno.test("advanceGoalInstances - does not mark non-pending instances as missed"
254274 createInstance ( goal , pastTime , pastEndTime , "completed" ) ;
255275 createInstance ( goal , pastTime , pastEndTime , "skipped" ) ;
256276
257- await advanceGoalInstances ( ) ;
277+ await advanceGoalInstances ( noopScheduler ) ;
258278
259279 const instances = db . prepare ( "SELECT * FROM goal_instances WHERE goal_id = ?" )
260280 . all ( goal . id ) ;
@@ -272,15 +292,15 @@ Deno.test("advanceGoalInstances - does not mark non-pending instances as missed"
272292} ) ;
273293
274294Deno . test ( "advanceGoalInstances - updates last_advance_at tracking column" , async ( ) => {
275- const { user } = await setupUser ( ) ;
295+ const { user } = setupUser ( ) ;
276296 const goal = createGoal ( user , "Tracking Goal" , 3 , "afternoon" ) ;
277297
278298 const pastTime = dayjs . utc ( ) . subtract ( 1 , "day" ) . toISOString ( ) ;
279299 const pastEndTime = dayjs . utc ( ) . subtract ( 1 , "day" ) . add ( 30 , "minute" )
280300 . toISOString ( ) ;
281301 createInstance ( goal , pastTime , pastEndTime ) ;
282302
283- await advanceGoalInstances ( ) ;
303+ await advanceGoalInstances ( noopScheduler ) ;
284304
285305 const updatedGoal = db . prepare (
286306 "SELECT last_advance_at FROM goals WHERE id = ?" ,
0 commit comments