Skip to content

Commit 3fe5d5a

Browse files
committed
fix テスト追加
1 parent 384c108 commit 3fe5d5a

8 files changed

Lines changed: 428 additions & 11 deletions

File tree

backend/cmd/subscriber/main_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestProcessMessage_UnknownType(t *testing.T) {
6060
payload := `{"type": "unknown_type", "user_id": "test"}`
6161

6262
// This should not return an error for unknown message types
63-
err := processMessage(ctx, nil, nil, nil, nil, payload, logger)
63+
err := processMessage(ctx, nil, nil, nil, nil, nil, payload, logger)
6464
if err != nil {
6565
t.Errorf("expected no error for unknown message type, got %v", err)
6666
}
@@ -73,7 +73,7 @@ func TestProcessMessage_InvalidJSON(t *testing.T) {
7373
payload := `invalid json`
7474

7575
// This should return an error for invalid JSON
76-
err := processMessage(ctx, nil, nil, nil, nil, payload, logger)
76+
err := processMessage(ctx, nil, nil, nil, nil, nil, payload, logger)
7777
if err == nil {
7878
t.Fatal("expected error for invalid JSON, got nil")
7979
}
@@ -86,7 +86,7 @@ func TestProcessMessage_LatestTrend_InvalidJSON(t *testing.T) {
8686
// latestTrendメッセージのJSONが不正な場合はエラーを返すことを確認
8787
payload := `{"type": "latest_trend", invalid_json}`
8888

89-
err := processMessage(ctx, nil, nil, nil, nil, payload, logger)
89+
err := processMessage(ctx, nil, nil, nil, nil, nil, payload, logger)
9090
if err == nil {
9191
t.Fatal("不正なJSONに対してエラーが期待されますが、nilが返りました")
9292
}
@@ -99,7 +99,7 @@ func TestProcessMessage_DiaryHighlight_InvalidJSON(t *testing.T) {
9999
// diaryHighlightメッセージのJSONが不正な場合はエラーを返すことを確認
100100
payload := `{"type": "diary_highlight", invalid_json}`
101101

102-
err := processMessage(ctx, nil, nil, nil, nil, payload, logger)
102+
err := processMessage(ctx, nil, nil, nil, nil, nil, payload, logger)
103103
if err == nil {
104104
t.Fatal("不正なJSONに対してエラーが期待されますが、nilが返りました")
105105
}

backend/infrastructure/database/diary_embeddings_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,89 @@ func TestSearchDiaryEntriesByEmbedding_LimitEdgeCases(t *testing.T) {
288288
})
289289
}
290290

291+
func TestSetHNSWEfSearch(t *testing.T) {
292+
db := testutil.SetupTestDB(t)
293+
ctx := context.Background()
294+
295+
t.Run("トランザクション内でhnsw.ef_searchを設定できる", func(t *testing.T) {
296+
tx, err := db.BeginTx(ctx, nil)
297+
if err != nil {
298+
t.Fatalf("トランザクション開始失敗: %v", err)
299+
}
300+
defer func() { _ = tx.Rollback() }()
301+
302+
if err := database.SetHNSWEfSearch(ctx, tx, 100); err != nil {
303+
t.Fatalf("SetHNSWEfSearch失敗: %v", err)
304+
}
305+
})
306+
}
307+
308+
func TestInsertSemanticSearchLog(t *testing.T) {
309+
db := testutil.SetupTestDB(t)
310+
ctx := context.Background()
311+
userID := testutil.CreateTestUser(t, db, "semantic-search-log@example.com", "User")
312+
313+
t.Run("意味的検索ログを挿入できる", func(t *testing.T) {
314+
if err := database.InsertSemanticSearchLog(ctx, db, userID); err != nil {
315+
t.Fatalf("InsertSemanticSearchLog失敗: %v", err)
316+
}
317+
318+
var count int
319+
if err := db.QueryRowContext(ctx, `SELECT COUNT(*) FROM semantic_search_logs WHERE user_id = $1`, userID).Scan(&count); err != nil {
320+
t.Fatalf("カウントクエリ失敗: %v", err)
321+
}
322+
if count != 1 {
323+
t.Errorf("期待 1, 実際 %d", count)
324+
}
325+
})
326+
}
327+
328+
func TestDiaryIDsWithoutEmbeddings(t *testing.T) {
329+
db := testutil.SetupTestDB(t)
330+
ctx := context.Background()
331+
userID := testutil.CreateTestUser(t, db, "diary-ids-without-embeddings@example.com", "User")
332+
333+
diaryID := uuid.New()
334+
now := time.Now().UnixMilli()
335+
if _, err := db.ExecContext(ctx,
336+
`INSERT INTO diaries (id, user_id, content, date, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)`,
337+
diaryID, userID, "embedding未生成の日記", "2024-01-01", now, now,
338+
); err != nil {
339+
t.Fatalf("日記の挿入に失敗: %v", err)
340+
}
341+
342+
t.Run("embedding未生成の日記IDを返す", func(t *testing.T) {
343+
ids, err := database.DiaryIDsWithoutEmbeddings(ctx, db, userID)
344+
if err != nil {
345+
t.Fatalf("DiaryIDsWithoutEmbeddings失敗: %v", err)
346+
}
347+
if len(ids) != 1 {
348+
t.Errorf("期待 1件, 実際 %d件", len(ids))
349+
}
350+
if len(ids) > 0 && ids[0] != diaryID.String() {
351+
t.Errorf("期待ID %s, 実際 %s", diaryID, ids[0])
352+
}
353+
})
354+
355+
t.Run("embedding生成済みの日記は含まない", func(t *testing.T) {
356+
dummyEmbedding := make([]float32, 3072)
357+
chunks := []database.DiaryChunk{
358+
{Index: 0, Content: "テスト", Summary: "テスト概要", Embedding: dummyEmbedding},
359+
}
360+
if err := database.UpsertDiaryChunkEmbeddings(ctx, db, diaryID, userID, chunks, "gemini-embedding-001"); err != nil {
361+
t.Fatalf("UpsertDiaryChunkEmbeddings失敗: %v", err)
362+
}
363+
364+
ids, err := database.DiaryIDsWithoutEmbeddings(ctx, db, userID)
365+
if err != nil {
366+
t.Fatalf("DiaryIDsWithoutEmbeddings失敗: %v", err)
367+
}
368+
if len(ids) != 0 {
369+
t.Errorf("embedding生成済みで0件を期待したが %d件", len(ids))
370+
}
371+
})
372+
}
373+
291374
func TestUpsertDiaryChunkEmbeddings_NonSQLDB(t *testing.T) {
292375
// db が *sql.DB でない場合にエラーが返ることを確認
293376
// NOTE: 現在の実装では *sql.DB 以外は受け付けないが、

backend/infrastructure/database/scheduler_queries_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,42 @@ func TestDiaryCountInDateRange(t *testing.T) {
155155
})
156156
}
157157

158+
func TestDiaryCountInMonth(t *testing.T) {
159+
db := testutil.SetupTestDB(t)
160+
ctx := context.Background()
161+
userID := testutil.CreateTestUser(t, db, "diary-count-in-month@example.com", "User")
162+
163+
now := time.Now().UnixMilli()
164+
for _, date := range []string{"2020-01-10", "2020-01-20"} {
165+
if _, err := db.ExecContext(ctx,
166+
`INSERT INTO diaries (id, user_id, content, date, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)`,
167+
uuid.New(), userID, "日記内容", date, now, now,
168+
); err != nil {
169+
t.Fatalf("日記の挿入に失敗: %v", err)
170+
}
171+
}
172+
173+
t.Run("指定月の日記件数を返す", func(t *testing.T) {
174+
count, err := database.DiaryCountInMonth(ctx, db, userID.String(), 2020, 1)
175+
if err != nil {
176+
t.Fatalf("DiaryCountInMonth失敗: %v", err)
177+
}
178+
if count != 2 {
179+
t.Errorf("期待 2, 実際 %d", count)
180+
}
181+
})
182+
183+
t.Run("日記が存在しない月は0を返す", func(t *testing.T) {
184+
count, err := database.DiaryCountInMonth(ctx, db, userID.String(), 2020, 2)
185+
if err != nil {
186+
t.Fatalf("DiaryCountInMonth失敗: %v", err)
187+
}
188+
if count != 0 {
189+
t.Errorf("期待 0, 実際 %d", count)
190+
}
191+
})
192+
}
193+
158194
func TestDiaryIDsNeedingEmbedding(t *testing.T) {
159195
db := testutil.SetupTestDB(t)
160196
ctx := context.Background()

backend/infrastructure/database/user_queries_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,46 @@ func TestPendingEmbeddingCount(t *testing.T) {
284284
}
285285
})
286286
}
287+
288+
func TestHourlyPubSubMetrics(t *testing.T) {
289+
db := testutil.SetupTestDB(t)
290+
ctx := context.Background()
291+
userID := testutil.CreateTestUser(t, db, "hourly-pubsub-metrics@example.com", "User")
292+
293+
t.Run("過去24時間の時間別メトリクスを返す(データなし)", func(t *testing.T) {
294+
metrics, err := database.HourlyPubSubMetrics(ctx, db, userID)
295+
if err != nil {
296+
t.Fatalf("HourlyPubSubMetrics失敗: %v", err)
297+
}
298+
// generate_seriesで24時間分返る
299+
if len(metrics) != 24 {
300+
t.Errorf("期待 24件, 実際 %d件", len(metrics))
301+
}
302+
// データがない場合は全てゼロ
303+
for _, m := range metrics {
304+
if m.DailySummariesProcessed != 0 || m.MonthlySummariesProcessed != 0 ||
305+
m.EmbeddingsProcessed != 0 || m.SemanticSearchesProcessed != 0 {
306+
t.Errorf("データなしで全ゼロを期待したが: %+v", m)
307+
}
308+
}
309+
})
310+
311+
t.Run("日次サマリーデータが集計に反映される", func(t *testing.T) {
312+
now := time.Now().Unix()
313+
if _, err := db.ExecContext(ctx,
314+
`INSERT INTO diary_summary_days (id, user_id, date, summary, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)`,
315+
uuid.New(), userID, "2020-01-01", "サマリ", now, now,
316+
); err != nil {
317+
t.Fatalf("日次サマリーの挿入に失敗: %v", err)
318+
}
319+
320+
// 直近1時間以内に挿入されたデータは集計に反映される
321+
recentMetrics, err := database.HourlyPubSubMetrics(ctx, db, userID)
322+
if err != nil {
323+
t.Fatalf("HourlyPubSubMetrics失敗: %v", err)
324+
}
325+
if len(recentMetrics) != 24 {
326+
t.Errorf("期待 24件, 実際 %d件", len(recentMetrics))
327+
}
328+
})
329+
}

backend/service/diary/embedding_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import (
66
"testing"
77
"time"
88

9+
"github.com/alicebob/miniredis/v2"
910
"github.com/google/uuid"
1011
g "github.com/project-mikan/umi.mikan/backend/infrastructure/grpc"
1112
"github.com/project-mikan/umi.mikan/backend/testutil"
13+
"github.com/redis/rueidis"
1214
)
1315

1416
func TestIsTodayJST(t *testing.T) {
@@ -281,6 +283,24 @@ func TestDiaryEntry_SearchDiaryEntriesSemantic_NoLLMKey(t *testing.T) {
281283
}
282284
}
283285

286+
func TestDiaryEntry_GenerateMonthlySummary_NoDiaries(t *testing.T) {
287+
db := setupTestDB(t)
288+
userID := createTestUser(t, db)
289+
svc := &DiaryEntry{DB: db}
290+
ctx := createAuthenticatedContext(userID)
291+
292+
// LLMキーを作成(GenerateMonthlySummaryはLLMキーチェックを通過する必要がある)
293+
testutil.CreateTestUserLLM(t, db, userID, "test-api-key")
294+
295+
// 日記が存在しない過去月に対してサマリー生成を要求する
296+
_, err := svc.GenerateMonthlySummary(ctx, &g.GenerateMonthlySummaryRequest{
297+
Month: &g.YM{Year: 2020, Month: 1},
298+
})
299+
if err == nil {
300+
t.Error("日記が存在しない月でエラーが返らなかった")
301+
}
302+
}
303+
284304
func TestDiaryEntry_SearchDiaryEntriesSemantic_EmptyQuery(t *testing.T) {
285305
db := setupTestDB(t)
286306
userID := createTestUser(t, db)
@@ -294,3 +314,52 @@ func TestDiaryEntry_SearchDiaryEntriesSemantic_EmptyQuery(t *testing.T) {
294314
t.Error("空クエリでエラーが返らなかった")
295315
}
296316
}
317+
318+
func setupTestRedisForDiary(t *testing.T) rueidis.Client {
319+
mr, err := miniredis.Run()
320+
if err != nil {
321+
t.Fatalf("miniredis起動失敗: %v", err)
322+
}
323+
t.Cleanup(mr.Close)
324+
client, err := rueidis.NewClient(rueidis.ClientOption{
325+
InitAddress: []string{mr.Addr()},
326+
DisableCache: true,
327+
})
328+
if err != nil {
329+
t.Fatalf("rueidisクライアント作成失敗: %v", err)
330+
}
331+
t.Cleanup(client.Close)
332+
return client
333+
}
334+
335+
func TestDiaryEntry_RegenerateAllEmbeddings_SemanticEnabled(t *testing.T) {
336+
db := setupTestDB(t)
337+
userID := testutil.CreateTestUser(t, db, "regen-embedding@example.com", "Regen Test User")
338+
ctx := createAuthenticatedContext(userID)
339+
340+
// semantic_search_enabled=trueでuser_llmsを直接挿入
341+
now := time.Now().Unix()
342+
if _, err := db.ExecContext(ctx,
343+
`INSERT INTO user_llms (user_id, llm_provider, key, auto_summary_daily, auto_summary_monthly, auto_latest_trend_enabled, semantic_search_enabled, created_at, updated_at)
344+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
345+
userID, 1, "test-api-key", false, false, false, true, now, now,
346+
); err != nil {
347+
t.Fatalf("user_llmsの挿入に失敗: %v", err)
348+
}
349+
350+
redisClient := setupTestRedisForDiary(t)
351+
svc := &DiaryEntry{DB: db, Redis: redisClient}
352+
353+
t.Run("正常系:日記が存在しない場合はQueuedCount=0で成功する", func(t *testing.T) {
354+
resp, err := svc.RegenerateAllEmbeddings(ctx, &g.RegenerateAllEmbeddingsRequest{})
355+
if err != nil {
356+
t.Fatalf("予期しないエラー: %v", err)
357+
}
358+
if !resp.Success {
359+
t.Error("Successがfalseになっている")
360+
}
361+
if resp.QueuedCount != 0 {
362+
t.Errorf("QueuedCount: got %d, want 0", resp.QueuedCount)
363+
}
364+
})
365+
}

backend/service/diary/service.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,13 +1230,7 @@ func (s *DiaryEntry) RegenerateAllEmbeddings(
12301230
}
12311231

12321232
// 件数に応じてロックTTLを動的に計算する(1件あたり10秒、最小10分・最大24時間)
1233-
lockTTL := time.Duration(len(diaryIDs)) * 10 * time.Second
1234-
if lockTTL < 10*time.Minute {
1235-
lockTTL = 10 * time.Minute
1236-
}
1237-
if lockTTL > 24*time.Hour {
1238-
lockTTL = 24 * time.Hour
1239-
}
1233+
lockTTL := min(max(time.Duration(len(diaryIDs))*10*time.Second, 10*time.Minute), 24*time.Hour)
12401234

12411235
// 同一ユーザーによる同時実行を防ぐ分散ロック
12421236
regenLock := lock.NewDistributedLock(s.Redis, lock.EmbeddingRegenLockKey(userIDStr), lockTTL)

0 commit comments

Comments
 (0)