|
7 | 7 | "testing" |
8 | 8 |
|
9 | 9 | "github.com/tae2089/code-context-graph/internal/model" |
| 10 | + "github.com/tae2089/code-context-graph/internal/paging" |
10 | 11 | "gorm.io/driver/sqlite" |
11 | 12 | "gorm.io/gorm" |
12 | 13 | gormlogger "gorm.io/gorm/logger" |
@@ -205,3 +206,80 @@ func TestAnalyze_NoCommunities(t *testing.T) { |
205 | 206 | t.Fatalf("expected 0, got %d", len(got)) |
206 | 207 | } |
207 | 208 | } |
| 209 | + |
| 210 | +func seedCrossPair(t *testing.T, db *gorm.DB, fromNode, toNode uint, count int, tag string) { |
| 211 | + t.Helper() |
| 212 | + for i := 0; i < count; i++ { |
| 213 | + if err := db.Create(&model.Edge{FromNodeID: fromNode, ToNodeID: toNode, Kind: model.EdgeKindCalls, Fingerprint: fmt.Sprintf("%s-%d", tag, i)}).Error; err != nil { |
| 214 | + t.Fatalf("seed edge: %v", err) |
| 215 | + } |
| 216 | + } |
| 217 | +} |
| 218 | + |
| 219 | +func TestAnalyzePage_AppliesLimitOffsetAndHasMore(t *testing.T) { |
| 220 | + db := setupDB(t) |
| 221 | + for i := uint(1); i <= 4; i++ { |
| 222 | + seedNode(t, db, i, fmt.Sprintf("N%d", i), fmt.Sprintf("c%d/c.go", i)) |
| 223 | + seedCommunity(t, db, i, fmt.Sprintf("c%d", i), i) |
| 224 | + } |
| 225 | + seedCrossPair(t, db, 1, 2, 10, "ab") |
| 226 | + seedCrossPair(t, db, 1, 3, 7, "ac") |
| 227 | + seedCrossPair(t, db, 1, 4, 4, "ad") |
| 228 | + seedCrossPair(t, db, 2, 3, 2, "bc") |
| 229 | + |
| 230 | + svc := New(db) |
| 231 | + |
| 232 | + page1, err := svc.AnalyzePage(context.Background(), paging.Request{Limit: 2, Offset: 0}) |
| 233 | + if err != nil { |
| 234 | + t.Fatalf("page1: %v", err) |
| 235 | + } |
| 236 | + if len(page1.Items) != 2 { |
| 237 | + t.Fatalf("page1 items=%d, want 2", len(page1.Items)) |
| 238 | + } |
| 239 | + if !page1.Pagination.HasMore { |
| 240 | + t.Fatalf("page1 has_more=false, want true") |
| 241 | + } |
| 242 | + if page1.Items[0].Strength < page1.Items[1].Strength { |
| 243 | + t.Fatalf("page1 not sorted by strength desc: %+v", page1.Items) |
| 244 | + } |
| 245 | + |
| 246 | + page2, err := svc.AnalyzePage(context.Background(), paging.Request{Limit: 2, Offset: 2}) |
| 247 | + if err != nil { |
| 248 | + t.Fatalf("page2: %v", err) |
| 249 | + } |
| 250 | + if len(page2.Items) != 2 { |
| 251 | + t.Fatalf("page2 items=%d, want 2", len(page2.Items)) |
| 252 | + } |
| 253 | + if page2.Pagination.HasMore { |
| 254 | + t.Fatalf("page2 has_more=true, want false") |
| 255 | + } |
| 256 | +} |
| 257 | + |
| 258 | +func TestAnalyzePage_RejectsLimitAboveMax(t *testing.T) { |
| 259 | + db := setupDB(t) |
| 260 | + svc := New(db) |
| 261 | + if _, err := svc.AnalyzePage(context.Background(), paging.Request{Limit: paging.MaxLimit + 1}); err == nil { |
| 262 | + t.Fatal("expected error for over-max limit") |
| 263 | + } |
| 264 | +} |
| 265 | + |
| 266 | +func TestAnalyzePage_OffsetBeyondTotalReturnsEmpty(t *testing.T) { |
| 267 | + db := setupDB(t) |
| 268 | + seedNode(t, db, 1, "A1", "a/a.go") |
| 269 | + seedNode(t, db, 2, "B1", "b/b.go") |
| 270 | + seedCommunity(t, db, 1, "a", 1) |
| 271 | + seedCommunity(t, db, 2, "b", 2) |
| 272 | + seedCrossPair(t, db, 1, 2, 3, "ab") |
| 273 | + |
| 274 | + svc := New(db) |
| 275 | + page, err := svc.AnalyzePage(context.Background(), paging.Request{Limit: 10, Offset: 50}) |
| 276 | + if err != nil { |
| 277 | + t.Fatal(err) |
| 278 | + } |
| 279 | + if len(page.Items) != 0 { |
| 280 | + t.Fatalf("items=%d, want 0", len(page.Items)) |
| 281 | + } |
| 282 | + if page.Pagination.HasMore { |
| 283 | + t.Fatal("has_more=true, want false") |
| 284 | + } |
| 285 | +} |
0 commit comments