package collector import ( "os" "path/filepath" "testing" ) func TestCountGenerations(t *testing.T) { dir := t.TempDir() // Create some generation symlinks for _, name := range []string{ "system-1-link", "system-2-link", "system-10-link", "system", // current system link, should not be counted "other-file", // unrelated file "system-x-link", // malformed, should not be counted } { path := filepath.Join(dir, name) if err := os.Symlink("/nix/store/dummy", path); err != nil { t.Fatal(err) } } count, err := countGenerations(dir) if err != nil { t.Fatal(err) } if count != 3 { t.Errorf("expected 3 generations, got %d", count) } } func TestGetCurrentGeneration(t *testing.T) { dir := t.TempDir() // Create system symlink pointing to a generation if err := os.Symlink("system-42-link", filepath.Join(dir, "system")); err != nil { t.Fatal(err) } gen, err := getCurrentGeneration(dir) if err != nil { t.Fatal(err) } if gen != 42 { t.Errorf("expected generation 42, got %d", gen) } } func TestGetBootedGeneration(t *testing.T) { profileDir := t.TempDir() bootedDir := t.TempDir() storePath := "/nix/store/abc123-nixos-system" // Create generation symlinks if err := os.Symlink("/nix/store/other", filepath.Join(profileDir, "system-1-link")); err != nil { t.Fatal(err) } if err := os.Symlink(storePath, filepath.Join(profileDir, "system-2-link")); err != nil { t.Fatal(err) } if err := os.Symlink("/nix/store/another", filepath.Join(profileDir, "system-3-link")); err != nil { t.Fatal(err) } // Create booted-system symlink bootedSystemPath := filepath.Join(bootedDir, "booted-system") if err := os.Symlink(storePath, bootedSystemPath); err != nil { t.Fatal(err) } gen, err := getBootedGeneration(profileDir, bootedSystemPath) if err != nil { t.Fatal(err) } if gen != 2 { t.Errorf("expected booted generation 2, got %d", gen) } } func TestCheckConfigMismatch(t *testing.T) { dir := t.TempDir() currentPath := filepath.Join(dir, "current-system") bootedPath := filepath.Join(dir, "booted-system") // Same target = no mismatch if err := os.Symlink("/nix/store/same", currentPath); err != nil { t.Fatal(err) } if err := os.Symlink("/nix/store/same", bootedPath); err != nil { t.Fatal(err) } mismatch, err := checkConfigMismatch(currentPath, bootedPath) if err != nil { t.Fatal(err) } if mismatch { t.Error("expected no mismatch when targets are the same") } // Different targets = mismatch if err := os.Remove(currentPath); err != nil { t.Fatal(err) } if err := os.Symlink("/nix/store/different", currentPath); err != nil { t.Fatal(err) } mismatch, err = checkConfigMismatch(currentPath, bootedPath) if err != nil { t.Fatal(err) } if !mismatch { t.Error("expected mismatch when targets differ") } } func TestGetGenerationAge(t *testing.T) { dir := t.TempDir() // Create system symlink if err := os.Symlink("system-1-link", filepath.Join(dir, "system")); err != nil { t.Fatal(err) } age, err := getGenerationAge(dir) if err != nil { t.Fatal(err) } // Age should be very small since we just created it if age < 0 || age > 1 { t.Errorf("expected age close to 0, got %f", age) } } func TestGenerationPattern(t *testing.T) { tests := []struct { name string match bool genNum string }{ {"system-1-link", true, "1"}, {"system-42-link", true, "42"}, {"system-123-link", true, "123"}, {"system", false, ""}, {"system-link", false, ""}, {"system--link", false, ""}, {"system-abc-link", false, ""}, {"other-1-link", false, ""}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { matches := generationPattern.FindStringSubmatch(tt.name) if tt.match { if matches == nil { t.Errorf("expected %q to match", tt.name) } else if matches[1] != tt.genNum { t.Errorf("expected generation %q, got %q", tt.genNum, matches[1]) } } else { if matches != nil { t.Errorf("expected %q not to match", tt.name) } } }) } }