(t *testing.T)
| 1212 | func float64Ptr(v float64) *float64 { return &v } |
| 1213 | |
| 1214 | func TestLaunchHeartbeat(t *testing.T) { |
| 1215 | t.Parallel() |
| 1216 | |
| 1217 | t.Run("fires_touch_step_on_tick", func(t *testing.T) { |
| 1218 | t.Parallel() |
| 1219 | |
| 1220 | ctrl := gomock.NewController(t) |
| 1221 | db := dbmock.NewMockStore(ctrl) |
| 1222 | mClock := quartz.NewMock(t) |
| 1223 | |
| 1224 | // Use a small stale threshold so the heartbeat interval is |
| 1225 | // short enough to test easily (threshold/2 = 5s, clamped ≥1s). |
| 1226 | svc := NewService(db, testutil.Logger(t), nil, |
| 1227 | WithClock(mClock), |
| 1228 | WithStaleThreshold(10*time.Second), |
| 1229 | ) |
| 1230 | |
| 1231 | stepID := uuid.New() |
| 1232 | runID := uuid.New() |
| 1233 | chatID := uuid.New() |
| 1234 | |
| 1235 | done := make(chan struct{}) |
| 1236 | defer close(done) |
| 1237 | |
| 1238 | // Trap the ticker creation so we can control it. |
| 1239 | tickerTrap := mClock.Trap().NewTicker("chatdebug", "heartbeat") |
| 1240 | defer tickerTrap.Close() |
| 1241 | |
| 1242 | ctx := testutil.Context(t, testutil.WaitShort) |
| 1243 | |
| 1244 | // Expect atomic TouchStep calls via TouchChatDebugStepAndRun. |
| 1245 | touchCalled := make(chan struct{}, 5) |
| 1246 | db.EXPECT(). |
| 1247 | TouchChatDebugStepAndRun(gomock.Any(), gomock.Any()). |
| 1248 | DoAndReturn(func(_ context.Context, params database.TouchChatDebugStepAndRunParams) error { |
| 1249 | require.Equal(t, stepID, params.StepID) |
| 1250 | require.Equal(t, runID, params.RunID) |
| 1251 | require.Equal(t, chatID, params.ChatID) |
| 1252 | select { |
| 1253 | case touchCalled <- struct{}{}: |
| 1254 | default: |
| 1255 | } |
| 1256 | return nil |
| 1257 | }). |
| 1258 | AnyTimes() |
| 1259 | |
| 1260 | launchHeartbeat(ctx, svc, stepID, runID, chatID, done) |
| 1261 | |
| 1262 | // Wait for the ticker to be created. |
| 1263 | tickerTrap.MustWait(ctx).MustRelease(ctx) |
| 1264 | |
| 1265 | // Advance the clock past one heartbeat interval (5s for a |
| 1266 | // 10s stale threshold) and verify TouchStep fires. |
| 1267 | mClock.Advance(5 * time.Second).MustWait(ctx) |
| 1268 | |
| 1269 | select { |
| 1270 | case <-touchCalled: |
| 1271 | case <-ctx.Done(): |
nothing calls this directly
no test coverage detected