~bigbes/ci-cacher

ref: 0ad1486d8a663154fb7e73cc694ffa2a20c01869 ci-cacher/internal/hash/hash_test.go -rw-r--r-- 3.4 KiB
0ad1486d — Eugene Blikh Add BSD-2-Clause license and README 2 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package hash

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// TestSingleFileMatchesSha256sumCutC116 asserts the central parity claim:
// Derive([f], 16) == `sha256sum f | cut -c1-16`. This is what the existing
// shell helper does (HASH=$(sha256sum docker/conformance.Dockerfile | cut -c1-16))
// and the new tool must produce the same key for the same input.
func TestSingleFileMatchesSha256sumCutC116(t *testing.T) {
	if _, err := exec.LookPath("sha256sum"); err != nil {
		t.Skip("sha256sum not on PATH; skipping shell parity check")
	}
	path := filepath.Join(t.TempDir(), "sample")
	if err := os.WriteFile(path, []byte("hello world\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	out, err := exec.Command("sha256sum", path).Output()
	if err != nil {
		t.Fatalf("sha256sum: %v", err)
	}
	want := strings.Fields(string(out))[0][:16]

	got, err := Derive([]string{path}, 16)
	if err != nil {
		t.Fatalf("Derive: %v", err)
	}
	if got != want {
		t.Errorf("Derive=%q  want=%q", got, want)
	}
}

func TestDirHashIsStable(t *testing.T) {
	dir := t.TempDir()
	mustWrite := func(rel, body string) {
		p := filepath.Join(dir, rel)
		if err := os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(p, []byte(body), 0o600); err != nil {
			t.Fatal(err)
		}
	}
	mustWrite("a/x", "x-body")
	mustWrite("b/y/z", "z-body")

	h1, err := Derive([]string{dir}, 32)
	if err != nil {
		t.Fatal(err)
	}
	h2, err := Derive([]string{dir}, 32)
	if err != nil {
		t.Fatal(err)
	}
	if h1 != h2 {
		t.Errorf("dir hash not stable: %q vs %q", h1, h2)
	}
}

func TestDirHashChangesOnContentChange(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "f")
	_ = os.WriteFile(path, []byte("v1"), 0o600)
	h1, _ := Derive([]string{dir}, 32)
	_ = os.WriteFile(path, []byte("v2"), 0o600)
	h2, _ := Derive([]string{dir}, 32)
	if h1 == h2 {
		t.Error("dir hash unchanged after content change")
	}
}

func TestMultiPathCombines(t *testing.T) {
	dir := t.TempDir()
	a := filepath.Join(dir, "a")
	b := filepath.Join(dir, "b")
	_ = os.WriteFile(a, []byte("A"), 0o600)
	_ = os.WriteFile(b, []byte("B"), 0o600)

	ha, _ := Derive([]string{a}, 16)
	hab, _ := Derive([]string{a, b}, 16)
	hba, _ := Derive([]string{b, a}, 16)
	if ha == hab {
		t.Error("combining two paths produced same hash as single path")
	}
	if hab == hba {
		t.Error("order of --hash-from paths should matter")
	}
}

func TestApplyTemplateHashPlaceholder(t *testing.T) {
	got := ApplyTemplate("img/{hash}.tar.zst", "abcd1234", "linux", "amd64", false)
	if got != "img/abcd1234.tar.zst" {
		t.Errorf("ApplyTemplate placeholder = %q", got)
	}
}

func TestApplyTemplateNoPlaceholderAppends(t *testing.T) {
	got := ApplyTemplate("img/build.tar.zst", "abcd1234", "linux", "amd64", false)
	if got != "img/build-abcd1234.tar.zst" {
		t.Errorf("ApplyTemplate append = %q", got)
	}
}

func TestApplyTemplateArchSuffix(t *testing.T) {
	got := ApplyTemplate("img/build.tar.zst", "abcd", "linux", "amd64", true)
	if got != "img/build-abcd-linux-amd64.tar.zst" {
		t.Errorf("ApplyTemplate arch = %q", got)
	}
}

func TestApplyTemplateArchSuffixSimpleExt(t *testing.T) {
	got := ApplyTemplate("bin/cacher", "", "linux", "amd64", true)
	if got != "bin/cacher-linux-amd64" {
		t.Errorf("ApplyTemplate arch no-ext = %q", got)
	}
}

func TestDeriveRejectsEmpty(t *testing.T) {
	if _, err := Derive(nil, 16); err == nil {
		t.Error("expected error for empty paths")
	}
}