package bulk import ( "context" "path/filepath" "testing" "time" "tangled.org/odd.computer/isomorph/internal/distro/debian" "tangled.org/odd.computer/isomorph/internal/model" ) func udd() []debian.UDDRow { return []debian.UDDRow{ { // DEP-12 Repository agrees with the homepage. Source: "json-c", Version: "0.18+ds-1", Homepage: "https://github.com/json-c/json-c/wiki", Binaries: []string{"libjson-c5", "libjson-c-dev"}, Metadata: map[string]string{"Repository": "https://github.com/json-c/json-c.git"}, WatchURL: "https://github.com/json-c/json-c/archive/refs/tags/json-c-0.18-20240915.tar.gz", }, { // Only a tarball watch URL on a known host. Source: "gzip", Version: "1.13-1", Homepage: "https://www.gnu.org/software/gzip/", WatchURL: "https://ftp.gnu.org/gnu/gzip/gzip-1.13.tar.xz", }, { // Nothing points at a repository; the packaging repo on salsa must not win. Source: "debian-native-tool", Version: "3", VcsURL: "https://salsa.debian.org/debian/debian-native-tool.git", }, { // Repository disagrees with the homepage's forge (a mirror). Source: "mirrored", Version: "1-1", Homepage: "https://github.com/someone/mirrored", Metadata: map[string]string{"Repository": "https://gitlab.com/upstream/mirrored.git"}, }, } } func TestFromUDD(t *testing.T) { pkgs := FromUDD(udd(), "sid") by := map[string]Package{} for _, p := range pkgs { by[p.Name] = p } j := by["json-c"] if j.UpstreamKey != "github.com/json-c/json-c" || j.Status != StatusResolved || j.Basis != BasisMetadata || j.OSVEcosystem != "Debian:sid" || j.PURL != "pkg:deb/debian/json-c?arch=source" || len(j.Binaries) != 2 { t.Errorf("json-c: %+v", j) } if g := by["gzip"]; g.UpstreamKey != "git.savannah.gnu.org/gzip" || g.Status != StatusResolved || g.Basis != BasisReleaseURL { t.Errorf("gzip via the GNU tarball hint: %+v", g) } if n := by["debian-native-tool"]; n.Status != StatusUnresolved || n.UpstreamKey != "" { t.Errorf("packaging repo taken as upstream: %+v", n) } if m := by["mirrored"]; m.UpstreamKey != "gitlab.com/upstream/mirrored" { t.Errorf("Repository must outweigh the homepage: %+v", m) } h := DebianHeldOut(udd()) if h.WithRepository != 2 || h.FallbackResolved != 2 || h.Agree != 1 || len(h.Disagree) != 1 || h.Disagree[0][0] != "mirrored" || h.ByBasis[BasisReleaseURL] != [2]int{1, 1} || h.ByBasis[BasisHomepage] != [2]int{1, 0} { t.Errorf("held out: %+v", h) } } func TestSQLiteRoundTrip(t *testing.T) { ctx := context.Background() db, err := Open(ctx, filepath.Join(t.TempDir(), "p.sqlite")) if err != nil { t.Fatal(err) } defer db.Close() deb := FromUDD(udd(), "sid") if err := Replace(ctx, db, Source{Distro: model.Debian, Suite: "sid", Origin: "test", FetchedAt: time.Now()}, deb); err != nil { t.Fatal(err) } arch := []Package{{Distro: model.Arch, Suite: "extra", Name: "json-c", Version: "0.18-1", UpstreamKey: "github.com/json-c/json-c", Status: StatusResolved, Basis: BasisVCSSource}} if err := Replace(ctx, db, Source{Distro: model.Arch, Suite: "extra", Origin: "test", FetchedAt: time.Now()}, arch); err != nil { t.Fatal(err) } got, err := Lookup(ctx, db, nil, "debian/sid:json-c", true) if err != nil || len(got.Matches) != 1 || got.Matches[0].UpstreamKey != "github.com/json-c/json-c" || len(got.Matches[0].Evidence) == 0 || got.Matches[0].Via != "package" || len(got.Counterparts) != 1 || got.Counterparts[0].Distro != model.Arch { t.Fatalf("lookup by package: %v %+v", err, got) } bin, err := Lookup(ctx, db, nil, "libjson-c5", false) if err != nil || len(bin.Matches) != 1 || bin.Matches[0].Name != "json-c" || bin.Matches[0].Via != "binary" || len(bin.Matches[0].Binaries) == 0 || len(bin.Matches[0].Evidence) != 0 { t.Fatalf("lookup by binary: %v %+v", err, bin) } for _, q := range []string{"github.com/json-c/json-c", "https://github.com/json-c/json-c.git", "https://github.com/json-c/json-c/archive/refs/tags/v1.tar.gz"} { both, err := Lookup(ctx, db, nil, q, false) if err != nil || len(both.Matches) != 2 || both.Matches[0].Via != "upstream" || len(both.Counterparts) != 0 { t.Fatalf("lookup by upstream %q: %v %+v", q, err, both) } } renamed, err := Lookup(ctx, db, map[string]string{"github.com/old/json-c": "github.com/json-c/json-c"}, "github.com/old/json-c", false) if err != nil || len(renamed.Matches) != 2 { t.Fatalf("lookup through an alias: %v %+v", err, renamed) } for _, q := range []string{"nosuch:json-c", "https://example.org/", ""} { if _, err := Lookup(ctx, db, nil, q, false); err == nil { t.Errorf("%q: expected an error", q) } } var n int if err := db.QueryRowContext(ctx, `select count(*) from counterparts where package = 'json-c'`).Scan(&n); err != nil || n != 2 { t.Fatalf("counterparts view: %v %d", err, n) } var binPkg string if err := db.QueryRowContext(ctx, `select package from binaries where binary = 'libjson-c5'`).Scan(&binPkg); err != nil || binPkg != "json-c" { t.Fatalf("binaries: %v %q", err, binPkg) } back, err := LoadSlice(ctx, db, model.Debian, "sid") if err != nil || len(back) != len(deb) { t.Fatalf("LoadSlice: %v %d", err, len(back)) } for i := range back { w := deb[i] for j := range deb { if deb[j].Name == back[i].Name { w = deb[j] } } if back[i].UpstreamKey != w.UpstreamKey || len(back[i].Evidence) != len(w.Evidence) || len(back[i].Binaries) != len(w.Binaries) || back[i].Basis != w.Basis { t.Errorf("LoadSlice %s: got %+v want %+v", w.Name, back[i], w) } } // Replacing a slice drops its previous rows. if err := Replace(ctx, db, Source{Distro: model.Debian, Suite: "sid", Origin: "test", FetchedAt: time.Now()}, deb[:1]); err != nil { t.Fatal(err) } if err := db.QueryRowContext(ctx, `select count(*) from packages where distro = 'debian'`).Scan(&n); err != nil || n != 1 { t.Fatalf("replace: %v %d", err, n) } } func TestOpenRefusesOtherSchemaVersions(t *testing.T) { ctx := context.Background() path := filepath.Join(t.TempDir(), "p.sqlite") db, err := Open(ctx, path) if err != nil { t.Fatal(err) } db.ExecContext(ctx, "pragma user_version = 1") db.Close() if _, err := Open(ctx, path); err == nil { t.Fatal("opened a database with another schema version") } } // TestStaleHomeHost: a SourceForge or Launchpad homepage outscores a GitHub release URL (the // URL counts as a hint at 0.6 * 0.6), but loses to it; a GitHub homepage keeps winning. func TestStaleHomeHost(t *testing.T) { pkgs := FromUDD([]debian.UDDRow{ {Source: "biber", Version: "2.22-1", Homepage: "http://biblatex-biber.sourceforge.net/", WatchURL: "https://api.github.com/repos/plk/biber/tarball/refs/tags/v2.22"}, {Source: "cloud-utils", Version: "0.34-1", Homepage: "https://launchpad.net/cloud-utils", WatchURL: "https://github.com/canonical/cloud-utils/archive/refs/tags/0.34.tar.gz"}, {Source: "vde2", Version: "2.3.3-1", Homepage: "http://vde.sourceforge.net"}, {Source: "gh", Version: "1-1", Homepage: "https://github.com/a/gh", WatchURL: "https://github.com/b/gh/archive/refs/tags/1.tar.gz"}, }, "sid") want := map[string][2]string{ "biber": {"github.com/plk/biber", BasisReleaseURL}, "cloud-utils": {"github.com/canonical/cloud-utils", BasisReleaseURL}, "vde2": {"sourceforge.net/vde", BasisHomepage}, "gh": {"github.com/a/gh", BasisHomepage}, } for _, p := range pkgs { if w := want[p.Name]; p.UpstreamKey != w[0] || p.Basis != w[1] { t.Errorf("%s: %s (%s), want %v", p.Name, p.UpstreamKey, p.Basis, w) } // Rescoring from the stored evidence keeps the choice. q := p q.UpstreamKey = "" Rescore(&q, nil) if q.UpstreamKey != p.UpstreamKey { t.Errorf("%s: rescored to %s", p.Name, q.UpstreamKey) } } }