From 5dd802ec9348319edb776d07e26bb4b0bd2fe83a Mon Sep 17 00:00:00 2001 From: Ahmad Rizqi Meydiarso Date: Wed, 7 May 2025 19:31:24 +0700 Subject: [PATCH 1/3] namespaced test and single test --- Makefile | 7 ++++-- internal/scheduler/dispatcher.go | 12 ++++++----- internal/scheduler/dispatcher_test.go | 16 ++++++++------ internal/scheduler/expander_test.go | 3 ++- internal/scheduler/scheduler.go | 26 +++++++++++----------- internal/scheduler/scheduler_test.go | 25 +++++++++++---------- internal/testutils/testutils.go | 7 +++++- scripts/test.sh | 31 ++++++++++++++++++--------- 8 files changed, 79 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index bb5074a..e1989fc 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ help: @echo "Available targets:" @echo " build Build the qhronosd binary" @echo " clean Remove the qhronosd binary" - @echo " test Run the test script (requires docker-up)" + @echo " test [name] Run the test script (requires docker-up). Optionally pass a test or subtest name, e.g. 'make test TestScheduler/successful scheduling'" @echo " migrate-up Run migrations up using scripts/migrate.sh" @echo " migrate-down Run migrations down using scripts/migrate.sh" @echo " migrate-clean-slate Drop and recreate the database, then run all migrations from scratch" @@ -28,7 +28,7 @@ clean: rm -f $(BINARY_NAME) test: - bash scripts/test.sh + bash scripts/test.sh $(filter-out $@,$(MAKECMDGOALS)) migrate-up: bash scripts/migrate.sh up @@ -56,3 +56,6 @@ redis-cleanup: docker-qup: docker compose up -d postgres redis qhronosd + +%:: + @: diff --git a/internal/scheduler/dispatcher.go b/internal/scheduler/dispatcher.go index 3db371c..2b4f54d 100644 --- a/internal/scheduler/dispatcher.go +++ b/internal/scheduler/dispatcher.go @@ -35,6 +35,7 @@ type Dispatcher struct { logger *zap.Logger clientNotifier ClientNotifier // optional, for q: webhooks scheduler *Scheduler // new field for scheduler + redisPrefix string // added redisPrefix field } // HTTPClient interface for mocking HTTP requests @@ -62,6 +63,7 @@ func NewDispatcher(eventRepo *repository.EventRepository, occurrenceRepo *reposi logger: logger, clientNotifier: clientNotifier, scheduler: scheduler, + redisPrefix: scheduler.redisPrefix, } } @@ -77,8 +79,8 @@ func (d *Dispatcher) DispatchWebhook(ctx context.Context, sched *models.Schedule if err != nil || event == nil { key := fmt.Sprintf("schedule:%s:%d", sched.EventID.String(), sched.ScheduledAt.Unix()) if d.scheduler != nil && d.scheduler.redis != nil { - _, zremErr := d.scheduler.redis.ZRem(ctx, ScheduleKey, key).Result() - _, hdelErr := d.scheduler.redis.HDel(ctx, "schedule:data", key).Result() + _, zremErr := d.scheduler.redis.ZRem(ctx, d.scheduler.redisPrefix+ScheduleKey, key).Result() + _, hdelErr := d.scheduler.redis.HDel(ctx, d.scheduler.redisPrefix+"schedule:data", key).Result() d.logger.Warn("Orphaned schedule found and removed", zap.String("event_id", sched.EventID.String()), zap.String("schedule_key", key), @@ -245,7 +247,7 @@ return due case <-ticker.C: now := fmt.Sprintf("%f", float64(time.Now().Unix())) // Use Lua script for atomic move - res, err := scheduler.redis.Eval(ctx, retryLua, []string{retryQueueKey, dispatchQueueKey}, now).Result() + res, err := scheduler.redis.Eval(ctx, retryLua, []string{d.scheduler.redisPrefix + retryQueueKey, d.scheduler.redisPrefix + dispatchQueueKey}, now).Result() if err != nil { d.logger.Error("[RETRY POLLER] Lua script failed", zap.Error(err)) continue @@ -267,7 +269,7 @@ return due d.logger.Debug("[DISPATCHER] Worker waiting for item", zap.Int("worker_id", workerID), zap.Time("ts", itemStart)) // Pop from dispatch queue (no processing queue) popStart := time.Now() - data, err := scheduler.redis.BRPop(ctx, 5*time.Second, dispatchQueueKey).Result() + data, err := scheduler.redis.BRPop(ctx, 5*time.Second, d.scheduler.redisPrefix+dispatchQueueKey).Result() popEnd := time.Now() d.logger.Debug("[DISPATCHER] BRPop duration", zap.Int("worker_id", workerID), zap.Duration("duration", popEnd.Sub(popStart)), zap.Error(err)) if err == redis.Nil { @@ -312,7 +314,7 @@ return due nextRetry := time.Now().Add(d.retryDelay).Unix() updatedData, _ := json.Marshal(sched) d.logger.Debug("[DISPATCHER] Before ZAdd to retry queue", zap.Int("worker_id", workerID), zap.Time("ts", zaddStart)) - err := scheduler.redis.ZAdd(ctx, retryQueueKey, redis.Z{ + err := scheduler.redis.ZAdd(ctx, d.scheduler.redisPrefix+retryQueueKey, redis.Z{ Score: float64(nextRetry), Member: updatedData, }).Err() diff --git a/internal/scheduler/dispatcher_test.go b/internal/scheduler/dispatcher_test.go index 39ec71b..a7eff2b 100644 --- a/internal/scheduler/dispatcher_test.go +++ b/internal/scheduler/dispatcher_test.go @@ -81,7 +81,8 @@ func TestDispatcher(t *testing.T) { hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - scheduler := NewScheduler(redisClient, logger) + namespace := testutils.GetRedisNamespace() + scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) @@ -365,12 +366,13 @@ func TestDispatcher_RedisOnlyDispatch(t *testing.T) { occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - scheduler := NewScheduler(redisClient, logger) + namespace := testutils.GetRedisNamespace() + scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) // Create Scheduler instance - scheduler = NewScheduler(redisClient, logger) + scheduler = NewScheduler(redisClient, logger, namespace) // Create event and schedule, schedule in Redis event := &models.Event{ @@ -439,12 +441,13 @@ func TestDispatcher_GetDueSchedules(t *testing.T) { occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - scheduler := NewScheduler(redisClient, logger) + namespace := testutils.GetRedisNamespace() + scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) // Create Scheduler instance - scheduler = NewScheduler(redisClient, logger) + scheduler = NewScheduler(redisClient, logger, namespace) // Create event and schedule, schedule in Redis event := &models.Event{ @@ -532,7 +535,8 @@ func TestDispatcher_DispatchQueueWorker(t *testing.T) { occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - scheduler := NewScheduler(redisClient, logger) + namespace := testutils.GetRedisNamespace() + scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) diff --git a/internal/scheduler/expander_test.go b/internal/scheduler/expander_test.go index 03611a0..fc2b48d 100644 --- a/internal/scheduler/expander_test.go +++ b/internal/scheduler/expander_test.go @@ -23,7 +23,8 @@ func TestEventExpander(t *testing.T) { redisClient := testutils.TestRedis(t) eventRepo := repository.NewEventRepository(db, logger, redisClient) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) - scheduler := NewScheduler(redisClient, logger) + namespace := testutils.GetRedisNamespace() + scheduler := NewScheduler(redisClient, logger, namespace) // Add cleanup function cleanup := func() { diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go index 2f4db76..974f932 100644 --- a/internal/scheduler/scheduler.go +++ b/internal/scheduler/scheduler.go @@ -19,14 +19,16 @@ const ( ) type Scheduler struct { - redis *redis.Client - logger *zap.Logger + redis *redis.Client + logger *zap.Logger + redisPrefix string } -func NewScheduler(redis *redis.Client, logger *zap.Logger) *Scheduler { +func NewScheduler(redis *redis.Client, logger *zap.Logger, prefix string) *Scheduler { return &Scheduler{ - redis: redis, - logger: logger, + redis: redis, + logger: logger, + redisPrefix: prefix, } } @@ -36,7 +38,7 @@ func (s *Scheduler) ScheduleEvent(ctx context.Context, occurrence *models.Occurr key := fmt.Sprintf("schedule:%s:%d", event.ID.String(), occurrence.ScheduledAt.Unix()) // Check if already scheduled - exists, err := s.redis.HExists(ctx, "schedule:data", key).Result() + exists, err := s.redis.HExists(ctx, s.redisPrefix+"schedule:data", key).Result() if err != nil { return fmt.Errorf("failed to check schedule existence: %w", err) } @@ -60,14 +62,14 @@ func (s *Scheduler) ScheduleEvent(ctx context.Context, occurrence *models.Occurr // Add to Redis sorted set and hash score := float64(occurrence.ScheduledAt.Unix()) - _, err = s.redis.ZAdd(ctx, ScheduleKey, redis.Z{ + _, err = s.redis.ZAdd(ctx, s.redisPrefix+ScheduleKey, redis.Z{ Score: score, Member: key, }).Result() if err != nil { return fmt.Errorf("failed to add schedule to sorted set: %w", err) } - _, err = s.redis.HSet(ctx, "schedule:data", key, data).Result() + _, err = s.redis.HSet(ctx, s.redisPrefix+"schedule:data", key, data).Result() if err != nil { return fmt.Errorf("failed to add schedule to hash: %w", err) } @@ -93,7 +95,7 @@ return count ` now := fmt.Sprintf("%d", time.Now().Unix()) - res, err := s.redis.Eval(ctx, scheduleLua, []string{ScheduleKey, "schedule:data", dispatchQueueKey}, now).Result() + res, err := s.redis.Eval(ctx, scheduleLua, []string{s.redisPrefix + ScheduleKey, s.redisPrefix + "schedule:data", s.redisPrefix + dispatchQueueKey}, now).Result() if err != nil { return 0, fmt.Errorf("failed to atomically move due schedules: %w", err) } @@ -105,7 +107,7 @@ return count // PopDispatchQueue pops a schedule from the dispatch queue (for worker use) func (s *Scheduler) PopDispatchQueue(ctx context.Context) (*models.Schedule, error) { - data, err := s.redis.LPop(ctx, dispatchQueueKey).Result() + data, err := s.redis.LPop(ctx, s.redisPrefix+dispatchQueueKey).Result() if err == redis.Nil { return nil, nil // No item } else if err != nil { @@ -121,11 +123,11 @@ func (s *Scheduler) PopDispatchQueue(ctx context.Context) (*models.Schedule, err // RemoveScheduledEvent removes a scheduled event from Redis (idempotent version) func (s *Scheduler) RemoveScheduledEvent(ctx context.Context, occurrence *models.Occurrence) error { key := fmt.Sprintf("schedule:%s:%d", occurrence.EventID.String(), occurrence.ScheduledAt.Unix()) - _, err := s.redis.ZRem(ctx, ScheduleKey, key).Result() + _, err := s.redis.ZRem(ctx, s.redisPrefix+ScheduleKey, key).Result() if err != nil { return fmt.Errorf("failed to remove schedule from sorted set: %w", err) } - _, err = s.redis.HDel(ctx, "schedule:data", key).Result() + _, err = s.redis.HDel(ctx, s.redisPrefix+"schedule:data", key).Result() if err != nil { return fmt.Errorf("failed to remove schedule from hash: %w", err) } diff --git a/internal/scheduler/scheduler_test.go b/internal/scheduler/scheduler_test.go index 9e66f7e..5fb0f36 100644 --- a/internal/scheduler/scheduler_test.go +++ b/internal/scheduler/scheduler_test.go @@ -21,13 +21,14 @@ import ( ) func TestScheduler(t *testing.T) { + namespace := testutils.GetRedisNamespace() cleanup := func() (*repository.EventRepository, *repository.OccurrenceRepository, *Scheduler, *zap.Logger, *testing.T, *redis.Client) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) eventRepo := repository.NewEventRepository(db, logger, redisClient) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) - scheduler := NewScheduler(redisClient, logger) + scheduler := NewScheduler(redisClient, logger, namespace) ctx := context.Background() _, err := db.ExecContext(ctx, "TRUNCATE TABLE events, occurrences CASCADE") require.NoError(t, err) @@ -131,7 +132,7 @@ func TestScheduler(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, count) // Check dispatch queue contents - items, err := redisClient.LRange(context.Background(), dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(context.Background(), namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 1) if len(items) == 0 { @@ -207,7 +208,7 @@ func TestScheduler(t *testing.T) { } // Get all scheduled occurrences from Redis - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, results, len(occurrences)) @@ -283,7 +284,7 @@ func TestScheduler(t *testing.T) { require.NoError(t, err) // There should be only one schedule in Redis - keys, err := redisClient.ZRange(context.Background(), ScheduleKey, 0, -1).Result() + keys, err := redisClient.ZRange(context.Background(), namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, keys, 1) }) @@ -325,7 +326,7 @@ func TestScheduler(t *testing.T) { assert.Equal(t, 1, count) // Check dispatch queue contents - items, err := redisClient.LRange(context.Background(), dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(context.Background(), namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 1) if len(items) == 0 { @@ -378,17 +379,18 @@ func TestScheduler(t *testing.T) { assert.Equal(t, occ.OccurrenceID, sched.OccurrenceID) // Queue should now be empty - items, err := redisClient.LRange(context.Background(), dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(context.Background(), namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 0) }) } func TestScheduler_AtomicGetDueSchedules_NoDuplicates(t *testing.T) { + namespace := testutils.GetRedisNamespace() ctx := context.Background() redisClient := testutils.TestRedis(t) redisClient.FlushAll(ctx) - scheduler := NewScheduler(redisClient, zap.NewNop()) + scheduler := NewScheduler(redisClient, zap.NewNop(), namespace) // Schedule 10 events due now now := time.Now() @@ -427,7 +429,7 @@ func TestScheduler_AtomicGetDueSchedules_NoDuplicates(t *testing.T) { wg.Wait() // Check that dispatch queue has exactly 10 unique items - items, err := redisClient.LRange(ctx, dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(ctx, namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 10) @@ -442,6 +444,7 @@ func TestScheduler_AtomicGetDueSchedules_NoDuplicates(t *testing.T) { } func TestScheduler_AtomicRetryPoller_NoDuplicates(t *testing.T) { + namespace := testutils.GetRedisNamespace() ctx := context.Background() redisClient := testutils.TestRedis(t) redisClient.FlushAll(ctx) @@ -477,7 +480,7 @@ func TestScheduler_AtomicRetryPoller_NoDuplicates(t *testing.T) { data, err := json.Marshal(sched) require.NoError(t, err) // Add to retry queue, due now - _, err = redisClient.ZAdd(ctx, retryQueueKey, redis.Z{ + _, err = redisClient.ZAdd(ctx, namespace+retryQueueKey, redis.Z{ Score: float64(now.Unix()), Member: data, }).Result() @@ -498,14 +501,14 @@ return due go func() { defer wg.Done() nowStr := fmt.Sprintf("%f", float64(now.Unix())) - _, err := redisClient.Eval(ctx, retryLua, []string{retryQueueKey, dispatchQueueKey}, nowStr).Result() + _, err := redisClient.Eval(ctx, retryLua, []string{namespace + retryQueueKey, namespace + dispatchQueueKey}, nowStr).Result() require.NoError(t, err) }() } wg.Wait() // Check that dispatch queue has exactly 1 item, no duplicates - items, err := redisClient.LRange(ctx, dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(ctx, namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 1) } diff --git a/internal/testutils/testutils.go b/internal/testutils/testutils.go index 92360b1..85ab217 100644 --- a/internal/testutils/testutils.go +++ b/internal/testutils/testutils.go @@ -61,4 +61,9 @@ func ReadMigrationSQL(t *testing.T) string { } return string(content) -} \ No newline at end of file +} + +// GetRedisNamespace returns the Redis namespace prefix for tests +func GetRedisNamespace() string { + return GetEnv("TEST_REDIS_NAMESPACE", "test:") +} diff --git a/scripts/test.sh b/scripts/test.sh index e34e559..8901f14 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -54,38 +54,49 @@ ALL_TEST_OUTPUT="" FAIL_OUTPUTS="" START_TIME=$(date +%s) + +# Accept optional test filter argument +TEST_FILTER="$1" + for PKG in $PKGS; do echo -e "\n=== Discovering subtests in $PKG ===" TEST_FILES=$(find $(go list -f '{{.Dir}}' $PKG) -name '*_test.go') + echo " Found test files: $TEST_FILES" TESTS_TO_RUN=() > /tmp/test_names.txt for file in $TEST_FILES; do parent_lines=$(grep -nE '^func Test[A-Za-z0-9_]*\(' "$file") - parent_count=$(echo "$parent_lines" | wc -l | tr -d ' ') - i=0 - echo "$parent_lines" | while read -r line; do + echo " Scanning $file for test functions..." + IFS=$'\n' read -rd '' -a parent_lines_arr <<<"$parent_lines" + parent_count=${#parent_lines_arr[@]} + for ((i=0; i> /tmp/test_names.txt + echo " Found test: $parent_name" else while read -r sub; do - [ -n "$sub" ] && echo "$parent_name/$sub" >> /tmp/test_names.txt + [ -n "$sub" ] && echo "$parent_name/$sub" >> /tmp/test_names.txt && echo " Found subtest: $parent_name/$sub" done <<< "$subtests" fi - i=$((i+1)) done done TESTS_TO_RUN=() while read -r testname; do - TESTS_TO_RUN+=("$testname") + # If TEST_FILTER is set, only add matching tests + if [ -z "$TEST_FILTER" ] || [[ "$testname" == *"$TEST_FILTER"* ]]; then + TESTS_TO_RUN+=("$testname") + fi done < /tmp/test_names.txt if [ ${#TESTS_TO_RUN[@]} -eq 0 ]; then echo "No test or subtest functions found in $PKG, skipping." From 81cc30607457334a53deb9cf4bcf1001e143f312 Mon Sep 17 00:00:00 2001 From: Ahmad Rizqi Meydiarso Date: Wed, 7 May 2025 19:39:23 +0700 Subject: [PATCH 2/3] change NewScheduler calling signature --- internal/handlers/event_handler_test.go | 4 +++- main.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/handlers/event_handler_test.go b/internal/handlers/event_handler_test.go index 42e3a07..7fb0185 100644 --- a/internal/handlers/event_handler_test.go +++ b/internal/handlers/event_handler_test.go @@ -30,9 +30,11 @@ func TestEventHandler(t *testing.T) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) + // Use test namespace for Redis keys in tests + namespace := testutils.GetRedisNamespace() eventRepo := repository.NewEventRepository(db, logger, redisClient) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) - schedulerService := scheduler.NewScheduler(redisClient, logger) + schedulerService := scheduler.NewScheduler(redisClient, logger, namespace) expander := scheduler.NewExpander( schedulerService, eventRepo, diff --git a/main.go b/main.go index 08e80d7..8588342 100644 --- a/main.go +++ b/main.go @@ -209,7 +209,7 @@ func main() { wsHandler := handlers.NewWebSocketHandler(db) // Initialize scheduler services - schedulerService := scheduler.NewScheduler(redisClient, logger) + schedulerService := scheduler.NewScheduler(redisClient, logger, "") expander := scheduler.NewExpander( schedulerService, eventRepo, From cf1d8474dd9fc482589788a6527854cc30d67362 Mon Sep 17 00:00:00 2001 From: Ahmad Rizqi Meydiarso Date: Thu, 8 May 2025 06:13:33 +0700 Subject: [PATCH 3/3] fix namespace test & support minute hour --- internal/handlers/event_handler_test.go | 2 +- internal/handlers/occurrence_handler_test.go | 3 +- .../integration/event_redis_cleanup_test.go | 13 ++++--- internal/repository/event_repository.go | 19 +++++----- internal/repository/event_repository_test.go | 12 ++++-- .../repository/occurrence_repository_test.go | 3 +- internal/scheduler/archiver_test.go | 3 +- internal/scheduler/dispatcher_test.go | 38 +++++++++---------- internal/scheduler/expander.go | 12 ++++++ internal/scheduler/expander_test.go | 30 +++++++-------- internal/scheduler/schedule.go | 8 +++- internal/scheduler/scheduler.go | 4 ++ internal/scheduler/scheduler_test.go | 2 +- main.go | 2 +- 14 files changed, 91 insertions(+), 60 deletions(-) diff --git a/internal/handlers/event_handler_test.go b/internal/handlers/event_handler_test.go index 7fb0185..df05215 100644 --- a/internal/handlers/event_handler_test.go +++ b/internal/handlers/event_handler_test.go @@ -32,7 +32,7 @@ func TestEventHandler(t *testing.T) { redisClient := testutils.TestRedis(t) // Use test namespace for Redis keys in tests namespace := testutils.GetRedisNamespace() - eventRepo := repository.NewEventRepository(db, logger, redisClient) + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) schedulerService := scheduler.NewScheduler(redisClient, logger, namespace) expander := scheduler.NewExpander( diff --git a/internal/handlers/occurrence_handler_test.go b/internal/handlers/occurrence_handler_test.go index fd5fe32..f7978ee 100644 --- a/internal/handlers/occurrence_handler_test.go +++ b/internal/handlers/occurrence_handler_test.go @@ -25,7 +25,8 @@ func TestOccurrenceHandler(t *testing.T) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) - eventRepo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) handler := NewOccurrenceHandler(eventRepo, occurrenceRepo) diff --git a/internal/integration/event_redis_cleanup_test.go b/internal/integration/event_redis_cleanup_test.go index bc97c48..db2a503 100644 --- a/internal/integration/event_redis_cleanup_test.go +++ b/internal/integration/event_redis_cleanup_test.go @@ -25,7 +25,8 @@ func TestEventRepository_RedisCleanupOnDelete(t *testing.T) { logger := zap.NewNop() // Use a real DB for event repo, but test Redis db := testutils.TestDB(t) - repo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + repo := repository.NewEventRepository(db, logger, redisClient, namespace) // Create event and schedule occurrences in Redis (inline logic) event := &models.Event{ @@ -65,17 +66,17 @@ func TestEventRepository_RedisCleanupOnDelete(t *testing.T) { require.NoError(t, err) key := "schedule:" + event.ID.String() + ":" + fmt.Sprintf("%d", occ.ScheduledAt.Unix()) score := float64(occ.ScheduledAt.UnixMilli()) - _, err = redisClient.ZAdd(ctx, "schedules", redis.Z{ + _, err = redisClient.ZAdd(ctx, namespace+"schedules", redis.Z{ Score: score, Member: key, }).Result() require.NoError(t, err) - _, err = redisClient.HSet(ctx, "schedule:data", key, data).Result() + _, err = redisClient.HSet(ctx, namespace+"schedule:data", key, data).Result() require.NoError(t, err) } // Ensure occurrences are in Redis - results, err := redisClient.ZRange(ctx, "schedules", 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+"schedules", 0, -1).Result() require.NoError(t, err) assert.NotEmpty(t, results) @@ -84,11 +85,11 @@ func TestEventRepository_RedisCleanupOnDelete(t *testing.T) { require.NoError(t, err) // Ensure occurrences for this event are removed from Redis - results, err = redisClient.ZRange(ctx, "schedules", 0, -1).Result() + results, err = redisClient.ZRange(ctx, namespace+"schedules", 0, -1).Result() require.NoError(t, err) for _, res := range results { // Fetch from hash instead of decoding directly - data, err := redisClient.HGet(ctx, "schedule:data", res).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", res).Result() if err != nil { continue // skip if not found } diff --git a/internal/repository/event_repository.go b/internal/repository/event_repository.go index 541a1d7..dca178b 100644 --- a/internal/repository/event_repository.go +++ b/internal/repository/event_repository.go @@ -16,13 +16,14 @@ import ( ) type EventRepository struct { - db *sqlx.DB - logger *zap.Logger - redis *redis.Client + db *sqlx.DB + logger *zap.Logger + redis *redis.Client + redisPrefix string } -func NewEventRepository(db *sqlx.DB, logger *zap.Logger, redis *redis.Client) *EventRepository { - return &EventRepository{db: db, logger: logger, redis: redis} +func NewEventRepository(db *sqlx.DB, logger *zap.Logger, redis *redis.Client, prefix string) *EventRepository { + return &EventRepository{db: db, logger: logger, redis: redis, redisPrefix: prefix} } func timePtr(t time.Time) *time.Time { @@ -174,13 +175,13 @@ func (r *EventRepository) Delete(ctx context.Context, id uuid.UUID) error { // RemoveEventOccurrencesFromRedis removes all scheduled occurrences for an event from Redis func (r *EventRepository) RemoveEventOccurrencesFromRedis(ctx context.Context, eventID uuid.UUID) error { - results, err := r.redis.ZRange(ctx, "schedules", 0, -1).Result() + results, err := r.redis.ZRange(ctx, r.redisPrefix+"schedules", 0, -1).Result() if err != nil { return err } for _, key := range results { // Fetch the schedule data from the hash - data, err := r.redis.HGet(ctx, "schedule:data", key).Result() + data, err := r.redis.HGet(ctx, r.redisPrefix+"schedule:data", key).Result() if err != nil { continue // skip if not found or error } @@ -192,8 +193,8 @@ func (r *EventRepository) RemoveEventOccurrencesFromRedis(ctx context.Context, e } if sched.EventID == eventID { // Remove from both sorted set and hash - r.redis.ZRem(ctx, "schedules", key) - r.redis.HDel(ctx, "schedule:data", key) + r.redis.ZRem(ctx, r.redisPrefix+"schedules", key) + r.redis.HDel(ctx, r.redisPrefix+"schedule:data", key) } } return nil diff --git a/internal/repository/event_repository_test.go b/internal/repository/event_repository_test.go index 4cc192e..d0c8640 100644 --- a/internal/repository/event_repository_test.go +++ b/internal/repository/event_repository_test.go @@ -32,7 +32,8 @@ func TestEventRepository(t *testing.T) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) - repo := NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + repo := NewEventRepository(db, logger, redisClient, namespace) // Add cleanup function cleanup := func() { @@ -398,7 +399,8 @@ func TestDeleteOldEvents(t *testing.T) { logger := zap.NewNop() redisClient := testutils.TestRedis(t) - repo := NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + repo := NewEventRepository(db, logger, redisClient, namespace) ctx := context.Background() // Create test data @@ -467,7 +469,8 @@ func TestDeleteOldOccurrences(t *testing.T) { logger := zap.NewNop() redisClient := testutils.TestRedis(t) - repo := NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + repo := NewEventRepository(db, logger, redisClient, namespace) ctx := context.Background() // Create test event @@ -544,6 +547,7 @@ func TestArchiveOldData(t *testing.T) { logger := zap.NewNop() redisClient := testutils.TestRedis(t) + namespace := testutils.GetRedisNamespace() // Insert an old event and occurrence oldEvent := &models.Event{ @@ -558,7 +562,7 @@ func TestArchiveOldData(t *testing.T) { Status: models.EventStatusActive, CreatedAt: time.Now().Add(-48 * time.Hour), } - eventRepo := NewEventRepository(db, logger, redisClient) + eventRepo := NewEventRepository(db, logger, redisClient, namespace) err := eventRepo.Create(ctx, oldEvent) require.NoError(t, err) diff --git a/internal/repository/occurrence_repository_test.go b/internal/repository/occurrence_repository_test.go index 95af147..0971541 100644 --- a/internal/repository/occurrence_repository_test.go +++ b/internal/repository/occurrence_repository_test.go @@ -18,7 +18,8 @@ func TestOccurrenceRepository(t *testing.T) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) - eventRepo := NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := NewEventRepository(db, logger, redisClient, namespace) repo := NewOccurrenceRepository(db, logger) cleanup := func() { diff --git a/internal/scheduler/archiver_test.go b/internal/scheduler/archiver_test.go index 1c7a05b..d4e9909 100644 --- a/internal/scheduler/archiver_test.go +++ b/internal/scheduler/archiver_test.go @@ -41,7 +41,8 @@ func TestArchivalScheduler(t *testing.T) { Status: models.EventStatusActive, CreatedAt: time.Now().Add(-48 * time.Hour), } - eventRepo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) err = eventRepo.Create(ctx, oldEvent) require.NoError(t, err) diff --git a/internal/scheduler/dispatcher_test.go b/internal/scheduler/dispatcher_test.go index a7eff2b..96e494b 100644 --- a/internal/scheduler/dispatcher_test.go +++ b/internal/scheduler/dispatcher_test.go @@ -76,12 +76,12 @@ func TestDispatcher(t *testing.T) { db := testutils.TestDB(t) logger, _ := zap.NewDevelopment() redisClient := testutils.TestRedis(t) - eventRepo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - namespace := testutils.GetRedisNamespace() scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) @@ -311,7 +311,7 @@ func TestDispatcher(t *testing.T) { } data, err := json.Marshal(schedule) require.NoError(t, err) - err = redisClient.RPush(ctx, dispatchQueueKey, data).Err() + err = redisClient.RPush(ctx, namespace+dispatchQueueKey, data).Err() require.NoError(t, err) // Run the worker for a short time to process retries runWorkerAndWait(ctx, dispatcher, scheduler, 20*time.Millisecond) @@ -362,11 +362,11 @@ func TestDispatcher_RedisOnlyDispatch(t *testing.T) { logger, _ := zap.NewDevelopment() redisClient := testutils.TestRedis(t) redisClient.FlushAll(ctx) - eventRepo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - namespace := testutils.GetRedisNamespace() scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) @@ -437,11 +437,11 @@ func TestDispatcher_GetDueSchedules(t *testing.T) { logger, _ := zap.NewDevelopment() redisClient := testutils.TestRedis(t) redisClient.FlushAll(ctx) - eventRepo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - namespace := testutils.GetRedisNamespace() scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) @@ -480,7 +480,7 @@ func TestDispatcher_GetDueSchedules(t *testing.T) { fmt.Printf("[DEBUG] Due schedules before dispatcher: %d\n", count) // Debug: Print contents of dispatch queue after GetDueSchedules - items, err := redisClient.LRange(ctx, dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(ctx, namespace+dispatchQueueKey, 0, -1).Result() if err != nil { t.Fatalf("Error reading dispatch queue: %v", err) } @@ -531,11 +531,11 @@ func TestDispatcher_DispatchQueueWorker(t *testing.T) { logger, _ := zap.NewDevelopment() redisClient := testutils.TestRedis(t) redisClient.FlushAll(ctx) - eventRepo := repository.NewEventRepository(db, logger, redisClient) + namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) hmacService := services.NewHMACService("test-secret") mockHTTP := new(MockHTTPClient) - namespace := testutils.GetRedisNamespace() scheduler := NewScheduler(redisClient, logger, namespace) dispatcher := NewDispatcher(eventRepo, occurrenceRepo, hmacService, logger, 3, 5*time.Second, nil, scheduler) dispatcher.SetHTTPClient(mockHTTP) @@ -588,17 +588,17 @@ func TestDispatcher_DispatchQueueWorker(t *testing.T) { } data, err := json.Marshal(sched) require.NoError(t, err) - err = redisClient.RPush(ctx, dispatchQueueKey, data).Err() + err = redisClient.RPush(ctx, namespace+dispatchQueueKey, data).Err() require.NoError(t, err) // Run worker and wait for completion runWorkerAndWait(ctx, dispatcher, scheduler, 3*time.Second) // After worker runs, dispatch queue and retry queue should be empty - items, err := redisClient.LRange(ctx, dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(ctx, namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 0) - retryItems, err := redisClient.ZRange(ctx, retryQueueKey, 0, -1).Result() + retryItems, err := redisClient.ZRange(ctx, namespace+retryQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, retryItems, 0) }) @@ -642,12 +642,12 @@ func TestDispatcher_DispatchQueueWorker(t *testing.T) { } data, err := json.Marshal(sched) require.NoError(t, err) - pushRes, err := redisClient.RPush(ctx, dispatchQueueKey, data).Result() + pushRes, err := redisClient.RPush(ctx, namespace+dispatchQueueKey, data).Result() require.NoError(t, err) fmt.Printf("[TEST DEBUG] RPush result: %v\n", pushRes) time.Sleep(100 * time.Millisecond) // Log the contents of the dispatch queue before starting the worker - items, err := redisClient.LRange(ctx, dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(ctx, namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) fmt.Printf("[TEST DEBUG] Dispatch queue before worker: %v\n", items) @@ -656,7 +656,7 @@ func TestDispatcher_DispatchQueueWorker(t *testing.T) { time.Sleep(50 * time.Millisecond) // Retry queue should have the item (since not yet max retries) - retryItems, err := redisClient.ZRange(ctx, retryQueueKey, 0, -1).Result() + retryItems, err := redisClient.ZRange(ctx, namespace+retryQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, retryItems, 1) }) @@ -707,17 +707,17 @@ func TestDispatcher_DispatchQueueWorker(t *testing.T) { } data, err := json.Marshal(sched) require.NoError(t, err) - err = redisClient.RPush(ctx, dispatchQueueKey, data).Err() + err = redisClient.RPush(ctx, namespace+dispatchQueueKey, data).Err() require.NoError(t, err) // Run worker and wait for enough time for maxRetries (2 seconds is more than enough now) runWorkerAndWait(ctx, dispatcher, scheduler, 2*time.Second) // After max retries, both dispatch and retry queues should be empty - items, err := redisClient.LRange(ctx, dispatchQueueKey, 0, -1).Result() + items, err := redisClient.LRange(ctx, namespace+dispatchQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, items, 0) - retryItems, err := redisClient.ZRange(ctx, retryQueueKey, 0, -1).Result() + retryItems, err := redisClient.ZRange(ctx, namespace+retryQueueKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, retryItems, 0) }) diff --git a/internal/scheduler/expander.go b/internal/scheduler/expander.go index 0f5b856..9c254fd 100644 --- a/internal/scheduler/expander.go +++ b/internal/scheduler/expander.go @@ -130,6 +130,18 @@ func (e *Expander) expandRecurringEvent(ctx context.Context, event *models.Event // Calculate occurrences based on frequency switch schedule.Frequency { + case "minute": + for t := startTime; t.Before(endTime); t = t.Add(time.Duration(schedule.Interval) * time.Minute) { + if t.After(graceStart) { + occurrences = append(occurrences, t) + } + } + case "hour": + for t := startTime; t.Before(endTime); t = t.Add(time.Duration(schedule.Interval) * time.Hour) { + if t.After(graceStart) { + occurrences = append(occurrences, t) + } + } case "daily": for t := startTime; t.Before(endTime); t = t.AddDate(0, 0, schedule.Interval) { if t.After(graceStart) { diff --git a/internal/scheduler/expander_test.go b/internal/scheduler/expander_test.go index fc2b48d..9ebef4b 100644 --- a/internal/scheduler/expander_test.go +++ b/internal/scheduler/expander_test.go @@ -21,9 +21,9 @@ func TestEventExpander(t *testing.T) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) - eventRepo := repository.NewEventRepository(db, logger, redisClient) - occurrenceRepo := repository.NewOccurrenceRepository(db, logger) namespace := testutils.GetRedisNamespace() + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) + occurrenceRepo := repository.NewOccurrenceRepository(db, logger) scheduler := NewScheduler(redisClient, logger, namespace) // Add cleanup function @@ -82,13 +82,13 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Get all events from Redis sorted set - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) assert.NotEmpty(t, results) // Verify at least one occurrence was created var occurrence models.Occurrence - data, err := redisClient.HGet(ctx, "schedule:data", results[0]).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", results[0]).Result() require.NoError(t, err) err = json.Unmarshal([]byte(data), &occurrence) require.NoError(t, err) @@ -102,7 +102,7 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Verify no occurrences were created - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) assert.Empty(t, results) }) @@ -131,13 +131,13 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Verify one occurrence was created in Redis - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, results, 1) // Verify the occurrence is for the correct event and scheduled at the correct time var occurrence models.Occurrence - data, err := redisClient.HGet(ctx, "schedule:data", results[0]).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", results[0]).Result() require.NoError(t, err) err = json.Unmarshal([]byte(data), &occurrence) require.NoError(t, err) @@ -169,13 +169,13 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Get all occurrences from Redis sorted set - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) assert.Len(t, results, 1) // Verify the occurrence is for the correct event and scheduled at the correct time var occurrence models.Occurrence - data, err := redisClient.HGet(ctx, "schedule:data", results[0]).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", results[0]).Result() require.NoError(t, err) err = json.Unmarshal([]byte(data), &occurrence) require.NoError(t, err) @@ -222,11 +222,11 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Get all scheduled occurrences from Redis - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) var scheduledMondays []time.Time for _, key := range results { - data, err := redisClient.HGet(ctx, "schedule:data", key).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", key).Result() require.NoError(t, err) var sched models.Schedule err = json.Unmarshal([]byte(data), &sched) @@ -280,11 +280,11 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Check that Monday is scheduled - results, err := redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err := redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) var scheduledDays []time.Weekday for _, key := range results { - data, err := redisClient.HGet(ctx, "schedule:data", key).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", key).Result() require.NoError(t, err) var sched models.Schedule err = json.Unmarshal([]byte(data), &sched) @@ -312,11 +312,11 @@ func TestEventExpander(t *testing.T) { require.NoError(t, err) // Check that only Wednesday is scheduled - results, err = redisClient.ZRange(ctx, ScheduleKey, 0, -1).Result() + results, err = redisClient.ZRange(ctx, namespace+ScheduleKey, 0, -1).Result() require.NoError(t, err) scheduledDays = scheduledDays[:0] for _, key := range results { - data, err := redisClient.HGet(ctx, "schedule:data", key).Result() + data, err := redisClient.HGet(ctx, namespace+"schedule:data", key).Result() require.NoError(t, err) var sched models.Schedule err = json.Unmarshal([]byte(data), &sched) diff --git a/internal/scheduler/schedule.go b/internal/scheduler/schedule.go index 528352c..4f99039 100644 --- a/internal/scheduler/schedule.go +++ b/internal/scheduler/schedule.go @@ -19,6 +19,8 @@ const ( type Frequency string const ( + FrequencyMinute Frequency = "minute" + FrequencyHour Frequency = "hour" FrequencyDaily Frequency = "daily" FrequencyWeekly Frequency = "weekly" FrequencyMonthly Frequency = "monthly" @@ -75,7 +77,7 @@ func ParseSchedule(scheduleStr string, logger *zap.Logger) (*Schedule, error) { func (s *Schedule) Validate() error { // Validate frequency switch s.Frequency { - case FrequencyDaily, FrequencyWeekly, FrequencyMonthly, FrequencyYearly: + case FrequencyMinute, FrequencyHour, FrequencyDaily, FrequencyWeekly, FrequencyMonthly, FrequencyYearly: default: return fmt.Errorf("invalid frequency: %s", s.Frequency) } @@ -148,6 +150,10 @@ func (s *Schedule) GetNextOccurrences(startTime time.Time, endTime time.Time, lo // Move to next interval switch s.Frequency { + case FrequencyMinute: + current = current.Add(time.Duration(s.Interval) * time.Minute) + case FrequencyHour: + current = current.Add(time.Duration(s.Interval) * time.Hour) case FrequencyDaily: current = current.AddDate(0, 0, s.Interval) case FrequencyWeekly: diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go index 974f932..2751297 100644 --- a/internal/scheduler/scheduler.go +++ b/internal/scheduler/scheduler.go @@ -177,6 +177,10 @@ func (s *Scheduler) calculateNextOccurrence(event *models.Event) (*time.Time, er // Handle different frequencies var nextTime time.Time switch schedule.Frequency { + case "minute": + nextTime = event.StartTime.Add(time.Duration(schedule.Interval) * time.Minute) + case "hour": + nextTime = event.StartTime.Add(time.Duration(schedule.Interval) * time.Hour) case "daily": nextTime = event.StartTime.AddDate(0, 0, schedule.Interval) case "weekly": diff --git a/internal/scheduler/scheduler_test.go b/internal/scheduler/scheduler_test.go index 5fb0f36..3547a57 100644 --- a/internal/scheduler/scheduler_test.go +++ b/internal/scheduler/scheduler_test.go @@ -26,7 +26,7 @@ func TestScheduler(t *testing.T) { db := testutils.TestDB(t) logger := zap.NewNop() redisClient := testutils.TestRedis(t) - eventRepo := repository.NewEventRepository(db, logger, redisClient) + eventRepo := repository.NewEventRepository(db, logger, redisClient, namespace) occurrenceRepo := repository.NewOccurrenceRepository(db, logger) scheduler := NewScheduler(redisClient, logger, namespace) ctx := context.Background() diff --git a/main.go b/main.go index 8588342..4366449 100644 --- a/main.go +++ b/main.go @@ -202,7 +202,7 @@ func main() { defer redisClient.Close() // Initialize repositories - eventRepo := repository.NewEventRepository(db, logger, redisClient) + eventRepo := repository.NewEventRepository(db, logger, redisClient, "") occurrenceRepo := repository.NewOccurrenceRepository(db, logger) // Initialize WebSocket handler (ClientNotifier)