Something went wrong. Try again.
Maps Linux distribution source packages to their upstream repositories, and through them to each other
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124package bulk
import ( "bufio" "context" "crypto/sha256" "encoding/hex" "encoding/json" "os" "path/filepath" "strings" "testing" "time"
"tangled.org/odd.computer/isomorph/internal/model")
func TestExport(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() at := time.Date(2026, 9, 28, 12, 0, 0, 0, time.UTC) if err := Replace(ctx, db, Source{Distro: model.Debian, Suite: "sid", Origin: "udd", FetchedAt: at}, FromUDD(udd(), "sid")); 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", Repo: "https://github.com/json-c/json-c", Status: StatusResolved, Basis: BasisVCSSource}} if err := Replace(ctx, db, Source{Distro: model.Arch, Suite: "extra", Origin: "pacman", FetchedAt: at}, arch); err != nil { t.Fatal(err) } out := t.TempDir() m, dir, err := Export(ctx, db, out, at) if err != nil { t.Fatal(err) } if !strings.HasPrefix(m.Snapshot, "20260928-") || dir != filepath.Join(out, m.Snapshot) || len(m.Slices) != 2 { t.Fatalf("manifest: %+v in %s", m, dir) } if m.Slices[0].Distro != "arch" || m.Slices[0].Packages != 1 || m.Slices[0].Resolved != 1 || m.Slices[1].Path != "debian-sid.jsonl" { t.Errorf("slices: %+v", m.Slices) } // Every file is where the manifest says, with the hash it says. for path, want := range map[string]string{m.Slices[0].Path: m.Slices[0].SHA256, m.Slices[1].Path: m.Slices[1].SHA256, m.Database.Path: m.Database.SHA256} { b, err := os.ReadFile(filepath.Join(dir, path)) if err != nil { t.Fatal(err) } if sum := sha256.Sum256(b); hex.EncodeToString(sum[:]) != want { t.Errorf("%s: sha256 differs from the manifest", path) } } var back Manifest if b, err := os.ReadFile(filepath.Join(dir, "manifest.json")); err != nil || json.Unmarshal(b, &back) != nil || back.Snapshot != m.Snapshot { t.Errorf("manifest.json: %v %+v", err, back) } for _, f := range []string{"package.schema.json", "manifest.schema.json"} { if _, err := os.Stat(filepath.Join(dir, f)); err != nil { t.Error(err) } } if b, _ := os.ReadFile(filepath.Join(out, "LATEST")); strings.TrimSpace(string(b)) != m.Snapshot { t.Errorf("LATEST = %q", b) } // Rows round-trip. f, err := os.Open(filepath.Join(dir, "debian-sid.jsonl")) if err != nil { t.Fatal(err) } defer f.Close() n := 0 for sc := bufio.NewScanner(f); sc.Scan(); n++ { var p Package if err := json.Unmarshal(sc.Bytes(), &p); err != nil || p.Distro != model.Debian { t.Fatalf("row %d: %v %+v", n, err, p) } } if n != m.Slices[1].Packages { t.Errorf("%d rows, manifest says %d", n, m.Slices[1].Packages) } // The same content exported again gets the same id. if again, _, err := Export(ctx, db, out, at.Add(time.Hour)); err != nil || again.Snapshot != m.Snapshot { t.Errorf("re-export: %v %s vs %s", err, again.Snapshot, m.Snapshot) }}
// TestOpenReadOnly: a reader neither writes the schema nor waits for a writer.func TestOpenReadOnly(t *testing.T) { ctx := context.Background() path := filepath.Join(t.TempDir(), "p.sqlite") if _, err := OpenReadOnly(ctx, path); err == nil { t.Fatal("a missing database must not be created") } w, err := Open(ctx, path) if err != nil { t.Fatal(err) } defer w.Close() if err := Replace(ctx, w, Source{Distro: model.Debian, Suite: "sid", Origin: "t", FetchedAt: time.Now()}, FromUDD(udd(), "sid")); err != nil { t.Fatal(err) } tx, err := w.BeginTx(ctx, nil) // a writer holding the database if err != nil { t.Fatal(err) } defer tx.Rollback() if _, err := tx.ExecContext(ctx, `insert into aliases values ('a','b','t','now')`); err != nil { t.Fatal(err) } r, err := OpenReadOnly(ctx, path) if err != nil { t.Fatal(err) } defer r.Close() if a, err := Lookup(ctx, r, nil, "debian:json-c", false); err != nil || len(a.Matches) != 1 { t.Fatalf("read while a writer holds the database: %v %+v", err, a) } if _, err := r.ExecContext(ctx, `delete from aliases`); err == nil { t.Error("a read-only database accepted a write") }}