142 lines
4.8 KiB
Go
142 lines
4.8 KiB
Go
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||
|
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||
|
|
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"net/url"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
// vendredi et samedi : deux instantanés d'un dépôt apt, à un jour d'écart.
|
||
|
|
const (
|
||
|
|
vendredi = "Fri, 11 Sep 2026 07:18:59 GMT"
|
||
|
|
samedi = "Sat, 12 Sep 2026 13:06:19 GMT"
|
||
|
|
)
|
||
|
|
|
||
|
|
// garnir range un corps sous son URL, avec la date que l'amont lui donne.
|
||
|
|
func garnir(t *testing.T, p *Proxy, brut, corps, lastModified string) {
|
||
|
|
t.Helper()
|
||
|
|
u, err := url.Parse(brut)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
w, err := p.Store.NewWriter(CleDe("GET", u), Meta{
|
||
|
|
URL: u.String(), Method: "GET", Status: http.StatusOK,
|
||
|
|
Header: http.Header{"Last-Modified": []string{lastModified}},
|
||
|
|
Class: ClassVolatile.String(),
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if _, err := w.Write([]byte(corps)); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := w.Commit(int64(len(corps))); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Un index plus récent que la signature qui l'annonce n'est pas servi.
|
||
|
|
//
|
||
|
|
// apt exige de chaque index la taille et la somme écrites dans la
|
||
|
|
// signature : un index pris à un autre instantané le fait échouer sur
|
||
|
|
// « File has unexpected size », et l'installation s'arrête plus bas sur des
|
||
|
|
// dépendances introuvables — un message qui accuse le dépôt, jamais le
|
||
|
|
// cache.
|
||
|
|
func TestUnIndexPlusRecentQueSaSignatureNeSortPas(t *testing.T) {
|
||
|
|
a := nouvelAmont(t, "peu importe")
|
||
|
|
p := proxyDeTest(t)
|
||
|
|
base := "http://" + a.hote() + "/ubuntu/dists/resolute-updates/"
|
||
|
|
garnir(t, p, base+"InRelease", "signature de vendredi", vendredi)
|
||
|
|
garnir(t, p, base+"main/binary-amd64/Packages.xz", "index de samedi", samedi)
|
||
|
|
hote := a.hote()
|
||
|
|
a.srv.Close()
|
||
|
|
|
||
|
|
w := joue(t, p, "GET", hote, "/ubuntu/dists/resolute-updates/InRelease")
|
||
|
|
if w.Code != http.StatusOK {
|
||
|
|
t.Fatalf("la signature gardée doit sortir : code %d", w.Code)
|
||
|
|
}
|
||
|
|
w = joue(t, p, "GET", hote,
|
||
|
|
"/ubuntu/dists/resolute-updates/main/binary-amd64/Packages.xz")
|
||
|
|
if w.Code == http.StatusOK {
|
||
|
|
t.Errorf("l'index de samedi est sorti sous la signature de vendredi")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// L'inverse est le cas NORMAL : les deux viennent du même passage en ligne,
|
||
|
|
// et refuser de servir là romprait le déploiement hors ligne.
|
||
|
|
func TestUnIndexAussiVieuxQueSaSignatureSort(t *testing.T) {
|
||
|
|
a := nouvelAmont(t, "peu importe")
|
||
|
|
p := proxyDeTest(t)
|
||
|
|
base := "http://" + a.hote() + "/ubuntu/dists/resolute/"
|
||
|
|
garnir(t, p, base+"InRelease", "signature", vendredi)
|
||
|
|
garnir(t, p, base+"main/binary-amd64/Packages.xz", "index", vendredi)
|
||
|
|
hote := a.hote()
|
||
|
|
a.srv.Close()
|
||
|
|
|
||
|
|
w := joue(t, p, "GET", hote,
|
||
|
|
"/ubuntu/dists/resolute/main/binary-amd64/Packages.xz")
|
||
|
|
if w.Code != http.StatusOK || w.Body.String() != "index" {
|
||
|
|
t.Fatalf("code %d, corps %q : l'index du même instantané doit sortir",
|
||
|
|
w.Code, w.Body.String())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Un objet adressé par son contenu ne se juge pas : son nom EST sa somme,
|
||
|
|
// et apt l'accepte quelle que soit la date de la signature.
|
||
|
|
func TestUnObjetParEmpreinteSortQuandMeme(t *testing.T) {
|
||
|
|
a := nouvelAmont(t, "peu importe")
|
||
|
|
p := proxyDeTest(t)
|
||
|
|
base := "http://" + a.hote() + "/ubuntu/dists/resolute-updates/"
|
||
|
|
garnir(t, p, base+"InRelease", "signature de vendredi", vendredi)
|
||
|
|
parHash := base + "main/binary-amd64/by-hash/SHA256/" +
|
||
|
|
"e55707cc7a5ad03d7f700ccb6646fd5c6ecbc852c4c826f5edbb5a321358d0ee"
|
||
|
|
garnir(t, p, parHash, "index de samedi", samedi)
|
||
|
|
hote := a.hote()
|
||
|
|
a.srv.Close()
|
||
|
|
|
||
|
|
w := joue(t, p, "GET", hote,
|
||
|
|
"/ubuntu/dists/resolute-updates/main/binary-amd64/by-hash/SHA256/"+
|
||
|
|
"e55707cc7a5ad03d7f700ccb6646fd5c6ecbc852c4c826f5edbb5a321358d0ee")
|
||
|
|
if w.Code != http.StatusOK {
|
||
|
|
t.Errorf("un objet par empreinte a été retenu : code %d", w.Code)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sans signature gardée, il n'y a rien à contredire : l'index gardé est tout
|
||
|
|
// ce que le hors-ligne possède, et le retenir ne servirait personne.
|
||
|
|
func TestSansSignatureLIndexSortQuandMeme(t *testing.T) {
|
||
|
|
a := nouvelAmont(t, "peu importe")
|
||
|
|
p := proxyDeTest(t)
|
||
|
|
garnir(t, p, "http://"+a.hote()+
|
||
|
|
"/ubuntu/dists/orpheline/main/binary-amd64/Packages.xz",
|
||
|
|
"index seul", samedi)
|
||
|
|
hote := a.hote()
|
||
|
|
a.srv.Close()
|
||
|
|
|
||
|
|
w := joue(t, p, "GET", hote,
|
||
|
|
"/ubuntu/dists/orpheline/main/binary-amd64/Packages.xz")
|
||
|
|
if w.Code != http.StatusOK || w.Body.String() != "index seul" {
|
||
|
|
t.Errorf("code %d, corps %q : l'index sans signature doit sortir",
|
||
|
|
w.Code, w.Body.String())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Hors de « /dists/ », aucune signature n'entre en jeu : un paquet ne se
|
||
|
|
// juge pas sur la date d'un index.
|
||
|
|
func TestHorsDeDistsRienNestJuge(t *testing.T) {
|
||
|
|
a := nouvelAmont(t, "peu importe")
|
||
|
|
p := proxyDeTest(t)
|
||
|
|
garnir(t, p, "http://"+a.hote()+"/ubuntu/pool/main/x/xz_5.6_amd64.deb",
|
||
|
|
"le paquet", samedi)
|
||
|
|
hote := a.hote()
|
||
|
|
a.srv.Close()
|
||
|
|
|
||
|
|
w := joue(t, p, "GET", hote, "/ubuntu/pool/main/x/xz_5.6_amd64.deb")
|
||
|
|
if w.Code != http.StatusOK {
|
||
|
|
t.Errorf("un paquet a été retenu : code %d", w.Code)
|
||
|
|
}
|
||
|
|
}
|