|
1 | | -package debutils_test |
| 1 | +package debutils |
2 | 2 |
|
3 | 3 | import ( |
4 | 4 | "fmt" |
|
9 | 9 |
|
10 | 10 | "github.com/open-edge-platform/os-image-composer/internal/config" |
11 | 11 | "github.com/open-edge-platform/os-image-composer/internal/ospackage" |
12 | | - "github.com/open-edge-platform/os-image-composer/internal/ospackage/debutils" |
13 | 12 | ) |
14 | 13 |
|
15 | 14 | func TestResolveDependenciesAdvanced(t *testing.T) { |
@@ -70,7 +69,7 @@ func TestResolveDependenciesAdvanced(t *testing.T) { |
70 | 69 |
|
71 | 70 | for _, tc := range testCases { |
72 | 71 | t.Run(tc.name, func(t *testing.T) { |
73 | | - result, err := debutils.ResolveDependencies(tc.requested, tc.all) |
| 72 | + result, err := ResolveDependencies(tc.requested, tc.all) |
74 | 73 |
|
75 | 74 | if tc.expectError { |
76 | 75 | if err == nil { |
@@ -161,7 +160,7 @@ func TestGenerateDot(t *testing.T) { |
161 | 160 |
|
162 | 161 | for _, tc := range testCases { |
163 | 162 | t.Run(tc.name, func(t *testing.T) { |
164 | | - err := debutils.GenerateDot(tc.pkgs, tc.filename, tc.pkgSources) |
| 163 | + err := GenerateDot(tc.pkgs, tc.filename, tc.pkgSources) |
165 | 164 | if tc.expectError { |
166 | 165 | if err == nil { |
167 | 166 | t.Errorf("expected error but got none") |
@@ -201,7 +200,7 @@ func TestGenerateDot(t *testing.T) { |
201 | 200 | // Check dependencies - each unique edge should appear exactly once |
202 | 201 | seenEdges := make(map[string]bool) |
203 | 202 | for _, dep := range pkg.Requires { |
204 | | - depName := debutils.CleanDependencyName(dep) |
| 203 | + depName := CleanDependencyName(dep) |
205 | 204 | if depName == "" { |
206 | 205 | continue |
207 | 206 | } |
@@ -268,7 +267,7 @@ func TestCleanDependencyName(t *testing.T) { |
268 | 267 |
|
269 | 268 | for _, tc := range testCases { |
270 | 269 | t.Run(tc.input, func(t *testing.T) { |
271 | | - result := debutils.CleanDependencyName(tc.input) |
| 270 | + result := CleanDependencyName(tc.input) |
272 | 271 | if result != tc.expected { |
273 | 272 | t.Errorf("cleanDependencyName(%q) = %q, expected %q", tc.input, result, tc.expected) |
274 | 273 | } |
@@ -325,7 +324,7 @@ func TestCompareDebianVersions(t *testing.T) { |
325 | 324 | for _, tc := range testCases { |
326 | 325 | t.Run(tc.a+"_vs_"+tc.b, func(t *testing.T) { |
327 | 326 | // Test the exported CompareDebianVersions function directly |
328 | | - result, err := debutils.CompareDebianVersions(tc.a, tc.b) |
| 327 | + result, err := CompareDebianVersions(tc.a, tc.b) |
329 | 328 | if err != nil { |
330 | 329 | t.Errorf("CompareDebianVersions(%q, %q) returned error: %v", tc.a, tc.b, err) |
331 | 330 | return |
@@ -380,6 +379,209 @@ func TestCompareVersions(t *testing.T) { |
380 | 379 | } |
381 | 380 | } |
382 | 381 |
|
| 382 | +func TestFilterCandidatesByPriorityWithTarget(t *testing.T) { |
| 383 | + // Mock repository configurations for testing |
| 384 | + oldRepoCfgs := RepoCfgs |
| 385 | + oldRepoCfg := RepoCfg |
| 386 | + defer func() { |
| 387 | + RepoCfgs = oldRepoCfgs |
| 388 | + RepoCfg = oldRepoCfg |
| 389 | + }() |
| 390 | + |
| 391 | + // Set up test repositories with different priorities |
| 392 | + RepoCfgs = []RepoConfig{ |
| 393 | + {PkgPrefix: "http://archive.ubuntu.com/ubuntu", Priority: 500}, // Default priority |
| 394 | + {PkgPrefix: "http://ppa.launchpad.net/test/ppa/ubuntu", Priority: 990}, // Preferred |
| 395 | + {PkgPrefix: "http://blocked.repo.com/ubuntu", Priority: -1}, // Blocked |
| 396 | + {PkgPrefix: "http://force.repo.com/ubuntu", Priority: 1001}, // Force install |
| 397 | + } |
| 398 | + |
| 399 | + testCases := []struct { |
| 400 | + name string |
| 401 | + candidates []ospackage.PackageInfo |
| 402 | + targetName string |
| 403 | + expectedNames []string // Expected order of package names after filtering |
| 404 | + expectedCount int |
| 405 | + description string |
| 406 | + }{ |
| 407 | + { |
| 408 | + name: "empty candidates", |
| 409 | + candidates: []ospackage.PackageInfo{}, |
| 410 | + targetName: "test-pkg", |
| 411 | + expectedNames: []string{}, |
| 412 | + expectedCount: 0, |
| 413 | + description: "should handle empty candidate list", |
| 414 | + }, |
| 415 | + { |
| 416 | + name: "exact name wins over provides", |
| 417 | + candidates: []ospackage.PackageInfo{ |
| 418 | + {Name: "linux-image-6.17.0-1017-oem", Version: "6.17.0-1017.18", |
| 419 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux-image-6.17.0-1017-oem_6.17.0-1017.18_amd64.deb", |
| 420 | + Provides: []string{"v4l2loopback-dkms"}}, |
| 421 | + {Name: "v4l2loopback-dkms", Version: "0.12.7-2ubuntu5.1", |
| 422 | + URL: "http://archive.ubuntu.com/ubuntu/pool/universe/v/v4l2loopback/v4l2loopback-dkms_0.12.7-2ubuntu5.1_all.deb"}, |
| 423 | + }, |
| 424 | + targetName: "v4l2loopback-dkms", |
| 425 | + expectedNames: []string{"v4l2loopback-dkms", "linux-image-6.17.0-1017-oem"}, |
| 426 | + expectedCount: 2, |
| 427 | + description: "exact name match should come first despite lower version number", |
| 428 | + }, |
| 429 | + { |
| 430 | + name: "blocked packages filtered out", |
| 431 | + candidates: []ospackage.PackageInfo{ |
| 432 | + {Name: "good-pkg", Version: "1.0", |
| 433 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/g/good/good-pkg_1.0_amd64.deb"}, |
| 434 | + {Name: "blocked-pkg", Version: "2.0", |
| 435 | + URL: "http://blocked.repo.com/ubuntu/pool/main/b/blocked/blocked-pkg_2.0_amd64.deb"}, |
| 436 | + }, |
| 437 | + targetName: "test-pkg", |
| 438 | + expectedNames: []string{"good-pkg"}, |
| 439 | + expectedCount: 1, |
| 440 | + description: "packages from blocked repositories should be filtered out", |
| 441 | + }, |
| 442 | + { |
| 443 | + name: "priority comparison for exact matches", |
| 444 | + candidates: []ospackage.PackageInfo{ |
| 445 | + {Name: "pkg", Version: "1.0", |
| 446 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/p/pkg/pkg_1.0_amd64.deb"}, // Priority 500 |
| 447 | + {Name: "pkg", Version: "1.0", |
| 448 | + URL: "http://ppa.launchpad.net/test/ppa/ubuntu/pool/main/p/pkg/pkg_1.0_amd64.deb"}, // Priority 990 |
| 449 | + }, |
| 450 | + targetName: "pkg", |
| 451 | + expectedNames: []string{"pkg", "pkg"}, // Higher priority first |
| 452 | + expectedCount: 2, |
| 453 | + description: "higher priority exact matches should come first", |
| 454 | + }, |
| 455 | + { |
| 456 | + name: "version comparison for exact matches", |
| 457 | + candidates: []ospackage.PackageInfo{ |
| 458 | + {Name: "pkg", Version: "1.0", |
| 459 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/p/pkg/pkg_1.0_amd64.deb"}, |
| 460 | + {Name: "pkg", Version: "2.0", |
| 461 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/p/pkg/pkg_2.0_amd64.deb"}, |
| 462 | + }, |
| 463 | + targetName: "pkg", |
| 464 | + expectedNames: []string{"pkg", "pkg"}, // Higher version first |
| 465 | + expectedCount: 2, |
| 466 | + description: "higher version exact matches should come first when same priority", |
| 467 | + }, |
| 468 | + { |
| 469 | + name: "force install priority", |
| 470 | + candidates: []ospackage.PackageInfo{ |
| 471 | + {Name: "pkg", Version: "1.0", |
| 472 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/p/pkg/pkg_1.0_amd64.deb"}, // Priority 500 |
| 473 | + {Name: "pkg", Version: "1.0", |
| 474 | + URL: "http://force.repo.com/ubuntu/pool/main/p/pkg/pkg_1.0_amd64.deb"}, // Priority 1001 (force) |
| 475 | + }, |
| 476 | + targetName: "pkg", |
| 477 | + expectedNames: []string{"pkg", "pkg"}, // Force install first |
| 478 | + expectedCount: 2, |
| 479 | + description: "force install packages should have highest priority", |
| 480 | + }, |
| 481 | + { |
| 482 | + name: "provides matches stable order", |
| 483 | + candidates: []ospackage.PackageInfo{ |
| 484 | + {Name: "kernel-a", Version: "6.17.0", |
| 485 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/k/kernel-a/kernel-a_6.17.0_amd64.deb", |
| 486 | + Provides: []string{"virtual-pkg"}}, |
| 487 | + {Name: "kernel-b", Version: "5.15.0", |
| 488 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/k/kernel-b/kernel-b_5.15.0_amd64.deb", |
| 489 | + Provides: []string{"virtual-pkg"}}, |
| 490 | + }, |
| 491 | + targetName: "virtual-pkg", |
| 492 | + expectedNames: []string{"kernel-a", "kernel-b"}, // Maintain stable order, no version comparison |
| 493 | + expectedCount: 2, |
| 494 | + description: "provides matches should maintain stable order without cross-type version comparison", |
| 495 | + }, |
| 496 | + { |
| 497 | + name: "mixed exact and provides with different priorities", |
| 498 | + candidates: []ospackage.PackageInfo{ |
| 499 | + {Name: "real-pkg", Version: "1.0", |
| 500 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/r/real/real-pkg_1.0_amd64.deb"}, // Priority 500 |
| 501 | + {Name: "high-priority-virtual", Version: "2.0", |
| 502 | + URL: "http://ppa.launchpad.net/test/ppa/ubuntu/pool/main/h/high/high-priority-virtual_2.0_amd64.deb", // Priority 990 |
| 503 | + Provides: []string{"real-pkg"}}, |
| 504 | + }, |
| 505 | + targetName: "real-pkg", |
| 506 | + expectedNames: []string{"real-pkg", "high-priority-virtual"}, // Exact match wins despite lower priority |
| 507 | + expectedCount: 2, |
| 508 | + description: "exact matches should win over provides matches regardless of priority", |
| 509 | + }, |
| 510 | + { |
| 511 | + name: "all candidates blocked", |
| 512 | + candidates: []ospackage.PackageInfo{ |
| 513 | + {Name: "blocked1", Version: "1.0", |
| 514 | + URL: "http://blocked.repo.com/ubuntu/pool/main/b/blocked1/blocked1_1.0_amd64.deb"}, |
| 515 | + {Name: "blocked2", Version: "2.0", |
| 516 | + URL: "http://blocked.repo.com/ubuntu/pool/main/b/blocked2/blocked2_2.0_amd64.deb"}, |
| 517 | + }, |
| 518 | + targetName: "test-pkg", |
| 519 | + expectedNames: []string{}, |
| 520 | + expectedCount: 0, |
| 521 | + description: "should return empty when all candidates are blocked", |
| 522 | + }, |
| 523 | + { |
| 524 | + name: "real v4l2loopback-dkms scenario", |
| 525 | + candidates: []ospackage.PackageInfo{ |
| 526 | + // Multiple kernel packages providing v4l2loopback-dkms (with higher versions) |
| 527 | + {Name: "linux-image-unsigned-6.17.0-1017-oem", Version: "6.17.0-1017.18", |
| 528 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/l/linux-unsigned/linux-image-unsigned-6.17.0-1017-oem_6.17.0-1017.18_amd64.deb", |
| 529 | + Provides: []string{"v4l2loopback-dkms"}}, |
| 530 | + {Name: "linux-image-unsigned-6.15.0-1015-oem", Version: "6.15.0-1015.16", |
| 531 | + URL: "http://archive.ubuntu.com/ubuntu/pool/main/l/linux-unsigned/linux-image-unsigned-6.15.0-1015-oem_6.15.0-1015.16_amd64.deb", |
| 532 | + Provides: []string{"v4l2loopback-dkms"}}, |
| 533 | + // Real v4l2loopback-dkms package (with lower version number) |
| 534 | + {Name: "v4l2loopback-dkms", Version: "0.12.7-2ubuntu5.1", |
| 535 | + URL: "http://archive.ubuntu.com/ubuntu/pool/universe/v/v4l2loopback/v4l2loopback-dkms_0.12.7-2ubuntu5.1_all.deb"}, |
| 536 | + }, |
| 537 | + targetName: "v4l2loopback-dkms", |
| 538 | + expectedNames: []string{"v4l2loopback-dkms", "linux-image-unsigned-6.17.0-1017-oem", "linux-image-unsigned-6.15.0-1015-oem"}, |
| 539 | + expectedCount: 3, |
| 540 | + description: "real v4l2loopback-dkms package should be selected first despite kernel packages having higher version numbers", |
| 541 | + }, |
| 542 | + } |
| 543 | + |
| 544 | + for _, tc := range testCases { |
| 545 | + t.Run(tc.name, func(t *testing.T) { |
| 546 | + result := filterCandidatesByPriorityWithTarget(tc.candidates, tc.targetName) |
| 547 | + |
| 548 | + if len(result) != tc.expectedCount { |
| 549 | + t.Errorf("expected %d candidates, got %d", tc.expectedCount, len(result)) |
| 550 | + for i, pkg := range result { |
| 551 | + t.Logf(" result[%d]: %s (version: %s)", i, pkg.Name, pkg.Version) |
| 552 | + } |
| 553 | + } |
| 554 | + |
| 555 | + // Check that the order matches expected names |
| 556 | + for i, expected := range tc.expectedNames { |
| 557 | + if i >= len(result) { |
| 558 | + t.Errorf("expected candidate %d to be %s, but result has only %d candidates", i, expected, len(result)) |
| 559 | + continue |
| 560 | + } |
| 561 | + if result[i].Name != expected { |
| 562 | + t.Errorf("expected candidate %d to be %s, got %s", i, expected, result[i].Name) |
| 563 | + } |
| 564 | + } |
| 565 | + |
| 566 | + // Verify no blocked packages in result |
| 567 | + for _, pkg := range result { |
| 568 | + if strings.Contains(pkg.URL, "blocked.repo.com") { |
| 569 | + t.Errorf("blocked package %s should not be in result", pkg.Name) |
| 570 | + } |
| 571 | + } |
| 572 | + |
| 573 | + // For the real v4l2loopback-dkms scenario, verify the exact match comes first |
| 574 | + if tc.name == "real v4l2loopback-dkms scenario" && len(result) > 0 { |
| 575 | + if result[0].Name != "v4l2loopback-dkms" { |
| 576 | + t.Errorf("v4l2loopback-dkms should be first candidate, got %s", result[0].Name) |
| 577 | + } |
| 578 | + } |
| 579 | + |
| 580 | + t.Logf("Test '%s': %s", tc.name, tc.description) |
| 581 | + }) |
| 582 | + } |
| 583 | +} |
| 584 | + |
383 | 585 | func TestResolveTopPackageConflicts(t *testing.T) { |
384 | 586 | all := []ospackage.PackageInfo{ |
385 | 587 | {Name: "acct", Version: "6.6.4-5+b1", URL: "pool/main/a/acct/acct_6.6.4-5+b1_amd64.deb"}, |
@@ -433,7 +635,7 @@ func TestResolveTopPackageConflicts(t *testing.T) { |
433 | 635 |
|
434 | 636 | for _, tc := range testCases { |
435 | 637 | t.Run(tc.name, func(t *testing.T) { |
436 | | - result, found := debutils.ResolveTopPackageConflicts(tc.want, all) |
| 638 | + result, found := ResolveTopPackageConflicts(tc.want, all) |
437 | 639 |
|
438 | 640 | if found != tc.expectFound { |
439 | 641 | t.Errorf("ResolveTopPackageConflicts(%q) found=%v, expected found=%v", tc.want, found, tc.expectFound) |
|
0 commit comments